From 2586bc3fcc517d8a9aa52d85ee12ee839bfd7184 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 23 Apr 2015 16:42:53 -0600 Subject: [PATCH] USB hub fixes. Mostly dealing with setting the hub function address at the right time and powering up downstream ports --- drivers/usbhost/usbhost_devaddr.c | 19 ++- drivers/usbhost/usbhost_enumerate.c | 109 ++++++++++-------- drivers/usbhost/usbhost_hidkbd.c | 3 +- drivers/usbhost/usbhost_hidmouse.c | 3 +- drivers/usbhost/usbhost_hub.c | 172 +++++++++++++++------------- drivers/usbhost/usbhost_skeleton.c | 3 +- drivers/usbhost/usbhost_storage.c | 3 +- include/nuttx/usb/usbhost_devaddr.h | 10 +- 8 files changed, 177 insertions(+), 145 deletions(-) diff --git a/drivers/usbhost/usbhost_devaddr.c b/drivers/usbhost/usbhost_devaddr.c index 8a3c4d8fc4..c7bf5e249c 100644 --- a/drivers/usbhost/usbhost_devaddr.c +++ b/drivers/usbhost/usbhost_devaddr.c @@ -251,7 +251,8 @@ void usbhost_devaddr_initialize(FAR struct usbhost_roothubport_s *rhport) * newly connected and so is in need of a function address. * * Returned Value: - * Zero on success; a negated errno value is returned on failure. + * On success, a new device function address in the the range 0x01 to 0x7f + * is returned. On failure, a negated errno value is returned. * *******************************************************************************/ @@ -278,38 +279,34 @@ int usbhost_devaddr_create(FAR struct usbhost_hubport_s *hport) if (devaddr < 0) { udbg("ERROR: Failed to allocate a device address\n"); - return devaddr; } - /* Set the function address in the hub port structure */ - - hport->funcaddr = devaddr; - return OK; + return devaddr; } /******************************************************************************* * Name: usbhost_devaddr_destroy * * Description: - * Release a device address previously assigned to a hub port by - * usbhost_devaddr_create(). + * Release a device address previously assigned by usbhost_devaddr_create(). * * Input Parameters: * hport - A reference to a hub port structure from which a device has been * disconnected and so no longer needs the function address. + * devaddr - The address to be released. * * Returned Value: * None * *******************************************************************************/ -void usbhost_devaddr_destroy(FAR struct usbhost_hubport_s *hport) +void usbhost_devaddr_destroy(FAR struct usbhost_hubport_s *hport, uint8_t devaddr) { FAR struct usbhost_devaddr_s *devgen; /* Ignore bad device address */ - if (hport->funcaddr > 0 && hport->funcaddr < 0x7f) + if (devaddr > 0 && devaddr < 0x7f) { /* Get the address generation data from the root hub port */ @@ -323,7 +320,7 @@ void usbhost_devaddr_destroy(FAR struct usbhost_hubport_s *hport) /* Free the device address */ - usbhost_devaddr_free(devgen, hport->funcaddr); + usbhost_devaddr_free(devgen, devaddr); usbhost_givesem(devgen); } } diff --git a/drivers/usbhost/usbhost_enumerate.c b/drivers/usbhost/usbhost_enumerate.c index 56d84f9656..a95d84f971 100644 --- a/drivers/usbhost/usbhost_enumerate.c +++ b/drivers/usbhost/usbhost_enumerate.c @@ -250,7 +250,7 @@ static inline int usbhost_classbind(FAR struct usbhost_hubport_s *hport, /* Then bind the newly instantiated class instance */ ret = CLASS_CONNECT(devclass, configdesc, desclen); - if (ret != OK) + if (ret < 0) { /* On failures, call the class disconnect method which * should then free the allocated devclass instance. @@ -313,6 +313,7 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, unsigned int cfglen; uint8_t maxpacketsize; uint8_t descsize; + uint8_t funcaddr = 0; FAR uint8_t *buffer = NULL; int ret; @@ -323,14 +324,14 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, */ ret = DRVR_ALLOC(hport->drvr, (FAR uint8_t **)&ctrlreq, &maxlen); - if (ret != OK) + if (ret < 0) { udbg("DRVR_ALLOC failed: %d\n", ret); return ret; } ret = DRVR_ALLOC(hport->drvr, &buffer, &maxlen); - if (ret != OK) + if (ret < 0) { udbg("DRVR_ALLOC failed: %d\n", ret); goto errout; @@ -377,7 +378,7 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, usbhost_putle16(ctrlreq->len, descsize); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer); - if (ret != OK) + if (ret < 0) { udbg("ERROR: Failed to get device descriptor, length=%d: %d\n", descsize, ret); @@ -393,27 +394,6 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, DRVR_EP0CONFIGURE(hport->drvr, hport->ep0, 0, maxpacketsize); - /* Set the USB device address */ - - ctrlreq->type = USB_REQ_DIR_OUT | USB_REQ_RECIPIENT_DEVICE; - ctrlreq->req = USB_REQ_SETADDRESS; - usbhost_putle16(ctrlreq->value, ((uint16_t)hport->funcaddr << 8)); - usbhost_putle16(ctrlreq->index, 0); - usbhost_putle16(ctrlreq->len, 0); - - ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); - if (ret != OK) - { - udbg("ERROR: Failed to set address: %d\n"); - goto errout; - } - - usleep(2*1000); - - /* And reconfigure EP0 with the correct address */ - - DRVR_EP0CONFIGURE(hport->drvr, hport->ep0, hport->funcaddr, maxpacketsize); - /* Now read the full device descriptor (if we have not already done so) */ if (descsize < USB_SIZEOF_DEVDESC) @@ -425,7 +405,7 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, usbhost_putle16(ctrlreq->len, USB_SIZEOF_DEVDESC); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer); - if (ret != OK) + if (ret < 0) { udbg("ERROR: Failed to get device descriptor, length=%d: %d\n", USB_SIZEOF_DEVDESC, ret); @@ -441,10 +421,45 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, (void)usbhost_devdesc((struct usb_devdesc_s *)buffer, &id); - /* Get the configuration descriptor (only), index == 0. Should not be - * hard-coded! More logic is needed in order to handle devices with - * multiple configurations. - */ + /* Assign a function address to the device connected to this port */ + + funcaddr = usbhost_devaddr_create(hport); + if (funcaddr < 0) + { + udbg("ERROR: usbhost_devaddr_create failed: %d\n", ret); + goto errout; + } + + /* Set the USB device address */ + + ctrlreq->type = USB_REQ_DIR_OUT | USB_REQ_RECIPIENT_DEVICE; + ctrlreq->req = USB_REQ_SETADDRESS; + usbhost_putle16(ctrlreq->value, (uint16_t)funcaddr); + usbhost_putle16(ctrlreq->index, 0); + usbhost_putle16(ctrlreq->len, 0); + + ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); + if (ret < 0) + { + udbg("ERROR: Failed to set address: %d\n"); + goto errout; + } + + usleep(2*1000); + + /* Assign the function address to the port */ + + DEBUGASSERT(hport->funcaddr == 0 && funcaddr != 0); + hport->funcaddr = funcaddr; + + /* And reconfigure EP0 with the correct address */ + + DRVR_EP0CONFIGURE(hport->drvr, hport->ep0, hport->funcaddr, maxpacketsize); + + /* Get the configuration descriptor (only), index == 0. Should not be + * hard-coded! More logic is needed in order to handle devices with + * multiple configurations. + */ ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_RECIPIENT_DEVICE; ctrlreq->req = USB_REQ_GETDESCRIPTOR; @@ -453,7 +468,7 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, usbhost_putle16(ctrlreq->len, USB_SIZEOF_CFGDESC); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer); - if (ret != OK) + if (ret < 0) { udbg("ERROR: Failed to get configuration descriptor, length=%d: %d\n", USB_SIZEOF_CFGDESC, ret); @@ -476,7 +491,7 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, usbhost_putle16(ctrlreq->len, cfglen); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer); - if (ret != OK) + if (ret < 0) { udbg("ERROR: Failed to get configuration descriptor, length=%d: %d\n", cfglen, ret); @@ -492,7 +507,7 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, usbhost_putle16(ctrlreq->len, 0); ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); - if (ret != OK) + if (ret < 0) { udbg("ERROR: Failed to set configuration: %d\n", ret); goto errout; @@ -510,9 +525,9 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, */ ret = usbhost_configdesc(buffer, cfglen, &id); - if (ret != OK) + if (ret < 0) { - udbg("ERROR: Failed to read class identification: %d\n", ret); + udbg("ERROR: usbhost_configdesc failed: %d\n", ret); goto errout; } } @@ -521,28 +536,28 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, usleep(100*1000); - /* Assign a function address to the device connected to this port */ - - ret = usbhost_devaddr_create(hport); - if (ret < 0) - { - udbg("ERROR: Failed to allocate device address: %d\n", ret); - goto errout; - } - /* Parse the configuration descriptor and bind to the class instance for the * device. This needs to be the last thing done because the class driver * will begin configuring the device. */ ret = usbhost_classbind(hport, buffer, cfglen, &id, devclass); - if (ret != OK) + if (ret < 0) { - udbg("ERROR: usbhost_classbind returned %d\n", ret); - usbhost_devaddr_destroy(hport); + udbg("ERROR: usbhost_classbind failed %d\n", ret); } errout: + if (ret < 0) + { + /* Release the device function address on any failure */ + + usbhost_devaddr_destroy(hport, funcaddr); + hport->funcaddr = 0; + } + + /* Release temporary buffers in any event */ + if (buffer != NULL) { DRVR_FREE(hport->drvr, buffer); diff --git a/drivers/usbhost/usbhost_hidkbd.c b/drivers/usbhost/usbhost_hidkbd.c index 125dea683c..c5b8258404 100644 --- a/drivers/usbhost/usbhost_hidkbd.c +++ b/drivers/usbhost/usbhost_hidkbd.c @@ -1991,7 +1991,8 @@ static int usbhost_disconnected(struct usbhost_class_s *usbclass) /* Free the function address assigned to this device */ - usbhost_devaddr_destroy(hport); + usbhost_devaddr_destroy(hport, hport->funcaddr); + hport->funcaddr = 0; return OK; } diff --git a/drivers/usbhost/usbhost_hidmouse.c b/drivers/usbhost/usbhost_hidmouse.c index 0e74b1f369..5db2453f48 100644 --- a/drivers/usbhost/usbhost_hidmouse.c +++ b/drivers/usbhost/usbhost_hidmouse.c @@ -2070,7 +2070,8 @@ static int usbhost_disconnected(struct usbhost_class_s *usbclass) /* Free the function address assigned to this device */ - usbhost_devaddr_destroy(hport); + usbhost_devaddr_destroy(hport, hport->funcaddr); + hport->funcaddr = 0; return OK; } diff --git a/drivers/usbhost/usbhost_hub.c b/drivers/usbhost/usbhost_hub.c index 60abdb63ca..3e4d3c3d56 100644 --- a/drivers/usbhost/usbhost_hub.c +++ b/drivers/usbhost/usbhost_hub.c @@ -49,10 +49,9 @@ #include #include -#include #include #include -#include +#include #include #include @@ -71,6 +70,24 @@ # warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)" #endif +/* Perform polling actions on the low priority work queue, if configured */ + +#ifndef CONFIG_SCHED_LPWORK +# define POLL_WORK LPWORK +#else +# define POLL_WORK HPWORK +#endif + +#define POLL_DELAY MSEC2TICK(400) + +/* Perform event processing on the high priority work queue, if configured */ + +#ifndef CONFIG_SCHED_HPWORK +# define EVENT_WORK HPWORK +#else +# define EVENT_WORK LPWORK +#endif + /* Used in usbhost_cfgdesc() */ #define USBHOST_IFFOUND 0x01 /* Required I/F descriptor found */ @@ -98,10 +115,7 @@ struct usbhost_hubpriv_s volatile bool disconnected; /* TRUE: Device has been disconnected */ bool compounddev; /* Hub is part of compound device */ bool indicator; /* Port indicator */ - uint16_t pwrondelay; /* Power on wait time in ms */ - int16_t crefs; /* Reference count on the driver instance */ - sem_t exclsem; /* Used to maintain mutual exclusive access */ struct work_s work; /* Used for deferred callback work */ usbhost_ep_t intin; /* Interrupt IN endpoint */ @@ -134,8 +148,9 @@ static void usbhost_destroy(FAR void *arg); static inline int usbhost_cfgdesc(FAR struct usbhost_class_s *hubclass, FAR const uint8_t *configdesc, int desclen); static inline int usbhost_hubdesc(FAR struct usbhost_class_s *hubclass); -static inline int usbhost_hubpwr(FAR struct usbhost_class_s *hubclass, - bool on); +static inline int usbhost_hubpwr(FAR struct usbhost_hubpriv_s *priv, + FAR struct usbhost_hubport_s *hport, + bool on); static void usbhost_hub_event(FAR void *arg); static void usbhost_disconnect_event(FAR void *arg); @@ -220,7 +235,7 @@ static void usbhost_hport_deactivate(FAR struct usbhost_hubport_s *hport) /* Free the function address if one has been assigned */ - usbhost_devaddr_destroy(hport); + usbhost_devaddr_destroy(hport, hport->funcaddr); hport->funcaddr = 0; DEBUGASSERT(hport->devclass == NULL); @@ -294,7 +309,7 @@ static void usbhost_destroy(FAR void *arg) priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; hport = hubclass->hport; - uvdbg("crefs: %d\n", priv->crefs); + uvdbg("Destroying hub on port %d\n", hport->port); /* Destroy the interrupt IN endpoint */ @@ -638,8 +653,9 @@ static inline int usbhost_hubdesc(FAR struct usbhost_class_s *hubclass) * executed and the PORT_POWER feature would be turned on. * * Input Parameters: - * hubclass - The USB host class instance. - * on - True: enable power; false: Disablel power + * priv - The USB hub private data + * hport - The port on the parent hub where the this hub is connected. + * on - True: enable power; false: Disable power * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is @@ -650,53 +666,45 @@ static inline int usbhost_hubdesc(FAR struct usbhost_class_s *hubclass) * ****************************************************************************/ -static int usbhost_hubpwr(FAR struct usbhost_class_s *hubclass, bool on) +static int usbhost_hubpwr(FAR struct usbhost_hubpriv_s *priv, + FAR struct usbhost_hubport_s *hport, + bool on) { - FAR struct usbhost_hubpriv_s *priv; - FAR struct usbhost_hubport_s *hport; FAR struct usb_ctrlreq_s *ctrlreq; uint16_t req; int port; int ret; - DEBUGASSERT(hubclass != NULL); - priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; + /* Are we enabling or disabling power? */ - DEBUGASSERT(hubclass->hport); - hport = hubclass->hport; - - /* Don't bother trying to control the power of a root hub port */ - - if (!ROOTHUB(hport)) + if (on) { - if (on) - { - req = USB_REQ_SETFEATURE; - } - else - { - req = USB_REQ_CLEARFEATURE; - } + req = USB_REQ_SETFEATURE; + } + else + { + req = USB_REQ_CLEARFEATURE; + } - /* Enable/disable power to all downstream ports */ + /* Enable/disable power to all downstream ports */ - ctrlreq = priv->ctrlreq; - DEBUGASSERT(ctrlreq); + ctrlreq = priv->ctrlreq; + DEBUGASSERT(ctrlreq); - for (port = 1; port <= priv->nports; port++) - { - ctrlreq->type = USBHUB_REQ_TYPE_PORT; - ctrlreq->req = req; - usbhost_putle16(ctrlreq->value, (USBHUB_PORT_FEAT_POWER << 8)); - usbhost_putle16(ctrlreq->index, port); - usbhost_putle16(ctrlreq->len, 0); + for (port = 1; port <= priv->nports; port++) + { + ctrlreq->type = USBHUB_REQ_TYPE_PORT; + ctrlreq->req = req; + usbhost_putle16(ctrlreq->value, USBHUB_PORT_FEAT_POWER); + usbhost_putle16(ctrlreq->index, port); + usbhost_putle16(ctrlreq->len, 0); - ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); - if (ret != OK) - { - udbg("ERROR: Failed to power %d port %d: %d\n", on, port, ret); - return ret; - } + ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); + if (ret != OK) + { + udbg("ERROR: Failed to power %s port %d: %d\n", + on ? "UP" : "DOWN", port, ret); + return ret; } } @@ -1030,38 +1038,27 @@ static void usbhost_disconnect_event(FAR void *arg) DEBUGASSERT(hubclass != NULL); priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; + DEBUGASSERT(hubclass->hport); + hport = hubclass->hport; + /* Set an indication to any users of the device that the device is no * longer available. */ - flags = irqsave(); + flags = irqsave(); priv->disconnected = true; - /* Now check the number of references on the class instance. If it is one, - * then we can free the class instance now. Otherwise, we will have to - * wait until the holders of the references free them by closing the - * block driver. - */ + /* Free buffer for status change (INT) endpoint */ - ullvdbg("crefs: %d\n", priv->crefs); - if (priv->crefs == 1) - { - DEBUGASSERT(hubclass->hport); - hport = hubclass->hport; + DRVR_IOFREE(hport->drvr, priv->buffer); - /* Free buffer for status change (INT) endpoint */ + /* Disable power to all downstream ports */ - DRVR_IOFREE(hport->drvr, priv->buffer); + (void)usbhost_hubpwr(priv, hport, false); - /* Disable power to all downstream ports */ - - (void)usbhost_hubpwr(hubclass, false); - - /* Destroy the class instance */ - - usbhost_destroy(hubclass); - } + /* Destroy the class instance */ + usbhost_destroy(hubclass); irqrestore(flags); } @@ -1127,15 +1124,36 @@ static void usbhost_callback(FAR void *arg, int result) { FAR struct usbhost_class_s *hubclass; FAR struct usbhost_hubpriv_s *priv; + uint32_t delay = 0; + int qid = EVENT_WORK; DEBUGASSERT(arg != NULL); hubclass = (FAR struct usbhost_class_s *)arg; priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; + /* Check for a failure */ + if (result != OK) { ulldbg("ERROR: Transfer failed: %d\n", result); + + /* Indicate there there is nothing to do. So when the work is + * performed, nothing will happen other than we will set to receive + * the next event. + */ + priv->buffer[0] = 0; + + /* We don't know the nature of the failure, but we need to do all that + * we can do to avoid a CPU hog error loop. + * + * Use the low-priority work queue and delay polling for the next + * event. We want to use as little CPU bandwidth as possible in this + * case. + */ + + qid = POLL_WORK; + delay = POLL_DELAY; } /* The work structure should always be available since hub communications @@ -1145,8 +1163,8 @@ static void usbhost_callback(FAR void *arg, int result) if (work_available(&priv->work)) { - (void)work_queue(HPWORK, &priv->work, (worker_t)usbhost_hub_event, - hubclass, 0); + (void)work_queue(qid, &priv->work, (worker_t)usbhost_hub_event, + hubclass, delay); } } @@ -1227,10 +1245,6 @@ static FAR struct usbhost_class_s * goto errout_with_ctrlreq; } - /* The initial reference count is 1... One reference is held by the driver */ - - priv->crefs = 1; - /* Initialize semaphores (this works okay in the interrupt context) */ sem_init(&priv->exclsem, 0, 1); @@ -1306,6 +1320,9 @@ static int usbhost_connect(FAR struct usbhost_class_s *hubclass, DEBUGASSERT(hubclass != NULL); priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; + DEBUGASSERT(hubclass->hport); + hport = hubclass->hport; + DEBUGASSERT(configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); /* Parse the configuration descriptor to get the endpoints */ @@ -1327,7 +1344,7 @@ static int usbhost_connect(FAR struct usbhost_class_s *hubclass, /* Enable power to all downstream ports */ - ret = usbhost_hubpwr(hubclass, true); + ret = usbhost_hubpwr(priv, hport, true); if (ret != OK) { return ret; @@ -1335,9 +1352,6 @@ static int usbhost_connect(FAR struct usbhost_class_s *hubclass, /* Enable monitoring of port status change events */ - DEBUGASSERT(hubclass->hport); - hport = hubclass->hport; - ret = DRVR_ASYNCH(hport->drvr, priv->intin, (FAR uint8_t *)priv->ctrlreq, sizeof(struct usb_ctrlreq_s), usbhost_callback, hubclass); @@ -1384,8 +1398,8 @@ static int usbhost_disconnected(struct usbhost_class_s *hubclass) */ flags = irqsave(); - ret = work_queue(HPWORK, &priv->work, (worker_t)usbhost_disconnect_event, - hubclass, 0); + ret = work_queue(EVENT_WORK, &priv->work, + (worker_t)usbhost_disconnect_event, hubclass, 0); irqrestore(flags); return ret; } diff --git a/drivers/usbhost/usbhost_skeleton.c b/drivers/usbhost/usbhost_skeleton.c index 55667e574a..27aea3c61d 100644 --- a/drivers/usbhost/usbhost_skeleton.c +++ b/drivers/usbhost/usbhost_skeleton.c @@ -370,7 +370,8 @@ static void usbhost_destroy(FAR void *arg) /* Free the function address assigned to this device */ - usbhost_devaddr_destroy(hport); + usbhost_devaddr_destroy(hport, hport->funcaddr); + hport->funcaddr = 0; /* Destroy the semaphores */ diff --git a/drivers/usbhost/usbhost_storage.c b/drivers/usbhost/usbhost_storage.c index c6b58d322c..bfe2f53a3c 100644 --- a/drivers/usbhost/usbhost_storage.c +++ b/drivers/usbhost/usbhost_storage.c @@ -1857,7 +1857,8 @@ static int usbhost_disconnected(struct usbhost_class_s *usbclass) /* Free the function address assigned to this device */ - usbhost_devaddr_destroy(hport); + usbhost_devaddr_destroy(hport, hport->funcaddr); + hport->funcaddr = 0; return OK; } diff --git a/include/nuttx/usb/usbhost_devaddr.h b/include/nuttx/usb/usbhost_devaddr.h index 366d57c6a8..718d804cae 100644 --- a/include/nuttx/usb/usbhost_devaddr.h +++ b/include/nuttx/usb/usbhost_devaddr.h @@ -120,7 +120,8 @@ void usbhost_devaddr_initialize(FAR struct usbhost_roothubport_s *rhport); * newly connected and so is in need of a function address. * * Returned Value: - * Zero on success; a negated errno value is returned on failure. + * On success, a new device function address in the the range 0x01 to 0x7f + * is returned. On failure, a negated errno value is returned. * *******************************************************************************/ @@ -130,19 +131,20 @@ int usbhost_devaddr_create(FAR struct usbhost_hubport_s *hport); * Name: usbhost_devaddr_destroy * * Description: - * Release a device address previously assigned to a hub port by - * usbhost_devaddr_create(). + * Release a device address previously assigned by usbhost_devaddr_create(). * * Input Parameters: * hport - A reference to a hub port structure from which a device has been * disconnected and so no longer needs the function address. + * devaddr - The address to be released. * * Returned Value: * None * *******************************************************************************/ -void usbhost_devaddr_destroy(FAR struct usbhost_hubport_s *hport); +void usbhost_devaddr_destroy(FAR struct usbhost_hubport_s *hport, + uint8_t devaddr); #undef EXTERN #if defined(__cplusplus)