diff --git a/arch/arm/src/lpc17xx/lpc17_usbhost.c b/arch/arm/src/lpc17xx/lpc17_usbhost.c index 2d01cb852ff..3a76ba8f040 100644 --- a/arch/arm/src/lpc17xx/lpc17_usbhost.c +++ b/arch/arm/src/lpc17xx/lpc17_usbhost.c @@ -156,24 +156,29 @@ struct lpc17_usbhost_s /* This is the hub port description understood by class drivers */ - struct usbhost_roothubport_s hport; - - /* The bound device class driver */ - - struct usbhost_class_s *class; + struct usbhost_roothubport_s rhport; /* Driver status */ + volatile bool change; /* Connection change */ volatile bool connected; /* Connected to device */ volatile bool lowspeed; /* Low speed device attached. */ - volatile bool rhswait; /* TRUE: Thread is waiting for Root Hub Status change */ + volatile bool pscwait; /* TRUE: Thread is waiting for a port status change */ + #ifndef CONFIG_USBHOST_INT_DISABLE uint8_t ininterval; /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */ uint8_t outinterval; /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */ #endif + sem_t exclsem; /* Support mutually exclusive access */ - sem_t rhssem; /* Semaphore to wait Writeback Done Head event */ -}; + sem_t pscsem; /* Semaphore to wait Writeback Done Head event */ + +#ifdef CONFIG_USBHOST_HUB + /* Used to pass external hub port events */ + + volatile struct usbhost_hubport_s *hport; +#endif + }; /* The OCHI expects the size of an endpoint descriptor to be 16 bytes. * However, the size allocated for an endpoint descriptor is 32 bytes in @@ -303,8 +308,11 @@ static int lpc17_usbinterrupt(int irq, void *context); /* USB host controller operations **********************************************/ static int lpc17_wait(struct usbhost_connection_s *conn, - const bool *connected); -static int lpc17_enumerate(struct usbhost_connection_s *conn, int rhpndx); + struct usbhost_hubport_s **hport); +static int lpc17_rh_enumerate(struct usbhost_connection_s *conn, + struct usbhost_hubport_s *hport); +static int lpc17_enumerate(struct usbhost_connection_s *conn, + struct usbhost_hubport_s *hport); static int lpc17_ep0configure(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, uint8_t funcaddr, @@ -331,7 +339,11 @@ static int lpc17_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen, usbhost_asynch_t callback, FAR void *arg); #endif - +#ifdef CONFIG_USBHOST_HUB +static int lpc17_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected); +#endif static void lpc17_disconnect(struct usbhost_driver_s *drvr); /* Initialization **************************************************************/ @@ -902,7 +914,7 @@ static inline int lpc17_addinted(struct lpc17_usbhost_s *priv, regval &= ~OHCI_CTRL_PLE; lpc17_putreg(regval, LPC17_USBHOST_CTRL); - /* Get the quanitized interval value associated with this ED and save it + /* Get the quantized interval value associated with this ED and save it * in the ED. */ @@ -1366,13 +1378,14 @@ static int lpc17_usbinterrupt(int irq, void *context) ullvdbg("Connected\n"); priv->connected = true; + priv->change = true; /* Notify any waiters */ - if (priv->rhswait) + if (priv->pscwait) { - lpc17_givesem(&priv->rhssem); - priv->rhswait = false; + lpc17_givesem(&priv->pscsem); + priv->pscwait = false; } } else @@ -1396,24 +1409,25 @@ static int lpc17_usbinterrupt(int irq, void *context) ullvdbg("Disconnected\n"); priv->connected = false; + priv->change = true; priv->lowspeed = false; /* Are we bound to a class instance? */ - if (priv->class) + if (priv->rhport.hport.devclass) { /* Yes.. Disconnect the class */ - CLASS_DISCONNECTED(priv->class); - priv->class = NULL; + CLASS_DISCONNECTED(priv->rhport.hport.devclass); + priv->rhport.hport.devclass = NULL; } /* Notify any waiters for the Root Hub Status change event */ - if (priv->rhswait) + if (priv->pscwait) { - lpc17_givesem(&priv->rhssem); - priv->rhswait = false; + lpc17_givesem(&priv->pscsem); + priv->pscwait = false; } } else @@ -1521,19 +1535,20 @@ static int lpc17_usbinterrupt(int irq, void *context) * Name: lpc17_wait * * Description: - * Wait for a device to be connected or disconneced. + * Wait for a device to be connected or disconnected to/from a hub port. * * Input Parameters: * conn - The USB host connection instance obtained as a parameter from the call to * the USB driver initialization logic. - * connected - A pointer to a boolean value: TRUE: Wait for device to be - * connected; FALSE: wait for device to be disconnected + * hport - The location to return the hub port descriptor that detected the + * connection related event. * * Returned Values: - * Zero (OK) is returned when a device in connected. This function will not - * return until either (1) a device is connected or (2) some failure occurs. - * On a failure, a negated errno value is returned indicating the nature of - * the failure + * Zero (OK) is returned on success when a device in connected or + * disconnected. This function will not return until either (1) a device is + * connected or disconnect to/from any hub port or until (2) some failure + * occurs. On a failure, a negated errno value is returned indicating the + * nature of the failure * * Assumptions: * - Called from a single thread so no mutual exclusion is required. @@ -1542,26 +1557,60 @@ static int lpc17_usbinterrupt(int irq, void *context) *******************************************************************************/ static int lpc17_wait(struct usbhost_connection_s *conn, - const bool *connected) + struct usbhost_hubport_s **hport) { struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)&g_usbhost; + struct usbhost_hubport_s *connport; irqstate_t flags; - /* Are we already connected? */ - flags = irqsave(); - while (priv->connected == *connected) + for (;;) { - /* No... wait for the connection/disconnection */ + /* Is there a change in the connection state of the single root hub + * port? + */ - priv->rhswait = true; - lpc17_takesem(&priv->rhssem); + if (priv->change) + { + connport = &priv->rhport.hport; + + /* Yes. Remember the new state */ + + connport->connected = priv->connected; + priv->change = false; + + /* And return the root hub port */ + + *hport = connport; + irqrestore(flags); + + udbg("RHport Connected: %s\n", connport->connected ? "YES" : "NO"); + return OK; + } + +#ifdef CONFIG_USBHOST_HUB + /* Is a device connected to an external hub? */ + + if (priv->hport) + { + /* Yes.. return the external hub port */ + + connport = (struct usbhost_hubport_s *)priv->hport; + priv->hport = NULL; + + *hport = connport; + irqrestore(flags); + + udbg("Hub port Connected: %s\n", connport->connected ? "YES" : "NO"); + return OK; + } +#endif + + /* Wait for the next connection event */ + + priv->pscwait = true; + lpc17_takesem(&priv->pscsem); } - - irqrestore(flags); - - udbg("Connected:%s\n", priv->connected ? "YES" : "NO"); - return OK; } /******************************************************************************* @@ -1573,30 +1622,30 @@ static int lpc17_wait(struct usbhost_connection_s *conn, * extract the class ID info from the configuration descriptor, (3) call * usbhost_findclass() to find the class that supports this device, (4) * call the create() method on the struct usbhost_registry_s interface - * to get a class instance, and finally (5) call the configdesc() method + * to get a class instance, and finally (5) call the connect() method * of the struct usbhost_class_s interface. After that, the class is in * charge of the sequence of operations. * * Input Parameters: - * conn - The USB host connection instance obtained as a parameter from the call to - * the USB driver initialization logic. - * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n. + * conn - The USB host connection instance obtained as a parameter from + * the call to the USB driver initialization logic. + * hport - The descriptor of the hub port that has the newly connected + * device. * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is * returned indicating the nature of the failure * * Assumptions: - * - Only a single class bound to a single device is supported. - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. + * This function will *not* be called from an interrupt handler. * *******************************************************************************/ -static int lpc17_enumerate(struct usbhost_connection_s *conn, int rphndx) +static int lpc17_rh_enumerate(struct usbhost_connection_s *conn, + struct usbhost_hubport_s *hport) { struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)&g_usbhost; - DEBUGASSERT(priv && rphndx == 0); + DEBUGASSERT(conn != NULL && hport != NULL &&& hport->port == 0); /* Are we connected to a device? The caller should have called the wait() * method first to be assured that a device is connected. @@ -1626,13 +1675,42 @@ static int lpc17_enumerate(struct usbhost_connection_s *conn, int rphndx) lpc17_putreg(OHCI_RHPORTST_PRSC, LPC17_USBHOST_RHPORTST1); (void)usleep(200*1000); + return OK; +} - /* Let the common usbhost_enumerate do all of the real work. Note that the - * FunctionAddress (USB address) is hardcoded to one. +static int lpc17_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + int ret; + + DEBUGASSERT(hport); + + /* If this is a connection on the root hub, then we need to go to + * little more effort to get the device speed. If it is a connection + * on an external hub, then we already have that information. */ +#ifdef CONFIG_USBHOST_HUB + if (ROOTHUB(hport)) +#endif + { + ret = lpc17_rh_enumerate(conn, hport); + if (ret < 0) + { + return ret; + } + } + + /* Then let the common usbhost_enumerate do the real enumeration. */ + uvdbg("Enumerate the device\n"); - return usbhost_enumerate(&g_usbhost.hport.hport, &priv->class); + ret = usbhost_enumerate(hport, &hport->devclass); + if (ret < 0) + { + udbg("ERROR: Enumeration failed: %d\n", ret); + } + + return ret; } /************************************************************************************ @@ -2407,6 +2485,56 @@ static int lpc17_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, } #endif +/************************************************************************************ + * Name: lpc17_connect + * + * Description: + * New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * hport - The descriptor of the hub port that detected the connection + * related event + * connected - True: device connected; false: device disconnected + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_HUB +static int lpc17_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected) +{ + struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr; + DEBUGASSERT(priv != NULL && hport != NULL); + irqstate_t flags; + + /* Set the connected/disconnected flag */ + + hport->connected = connected; + ullvdbg("Hub port %d connected: %s\n", hport->port, connected ? "YES" : "NO"); + + /* Report the connection event */ + + flags = irqsave(); + priv->hport = hport; + if (priv->pscwait) + { + priv->pscwait = false; + lpc17_givesem(&priv->pscsem); + } + + irqrestore(flags); + return OK; +} +#endif + /******************************************************************************* * Name: lpc17_disconnect * @@ -2435,7 +2563,7 @@ static void lpc17_disconnect(struct usbhost_driver_s *drvr) struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr; DEBUGASSERT(priv); - priv->class = NULL; + priv->rhport.hport.devclass = NULL; } /******************************************************************************* @@ -2555,12 +2683,15 @@ struct usbhost_connection_s *lpc17_usbhost_initialize(int controller) drvr->transfer = lpc17_transfer; #ifdef CONFIG_USBHOST_ASYNCH drvr->asynch = lpc17_asynch; +#endif +#ifdef CONFIG_USBHOST_HUB + drvr->connect = lpc17_connect; #endif drvr->disconnect = lpc17_disconnect; /* Initialize the public port representation */ - hport = &priv->hport.hport; + hport = &priv->rhport.hport; hport->drvr = drvr; #ifdef CONFIG_USBHOST_HUB hport->parent = NULL; @@ -2570,11 +2701,11 @@ struct usbhost_connection_s *lpc17_usbhost_initialize(int controller) /* Initialize function address generation logic */ - usbhost_devaddr_initialize(&priv->hport); + usbhost_devaddr_initialize(&priv->rhport); /* Initialize semaphores */ - sem_init(&priv->rhssem, 0, 0); + sem_init(&priv->pscsem, 0, 0); sem_init(&priv->exclsem, 0, 1); #ifndef CONFIG_USBHOST_INT_DISABLE diff --git a/arch/arm/src/sama5/sam_ehci.c b/arch/arm/src/sama5/sam_ehci.c index e912cb7f888..cbc14efd6bc 100644 --- a/arch/arm/src/sama5/sam_ehci.c +++ b/arch/arm/src/sama5/sam_ehci.c @@ -235,10 +235,6 @@ struct sam_rhport_s /* This is the hub port description understood by class drivers */ struct usbhost_roothubport_s hport; - - /* The bound device class driver */ - - struct usbhost_class_s *class; }; /* This structure retains the overall state of the USB host controller */ @@ -246,6 +242,7 @@ struct sam_rhport_s struct sam_ehci_s { volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */ + sem_t exclsem; /* Support mutually exclusive access */ sem_t pscsem; /* Semaphore to wait for port status change events */ @@ -254,7 +251,13 @@ struct sam_ehci_s struct sam_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */ struct work_s work; /* Supports interrupt bottom half */ - /* Root hub ports */ +#ifdef CONFIG_USBHOST_HUB + /* Used to pass external hub port events */ + + volatile struct usbhost_hubport_s *hport; +#endif + + /* Root hub ports */ struct sam_rhport_s rhport[SAM_EHCI_NRHPORT]; }; @@ -371,8 +374,11 @@ static int sam_ehci_tophalf(int irq, FAR void *context); /* USB Host Controller Operations **********************************************/ static int sam_wait(FAR struct usbhost_connection_s *conn, - FAR const bool *connected); -static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx); + FAR struct usbhost_hubport_s **hport); +static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); +static int sam_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); static int sam_ep0configure(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, uint8_t funcaddr, uint16_t maxpacketsize); @@ -398,6 +404,11 @@ static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen, usbhost_asynch_t callback, FAR void *arg); #endif +#ifdef CONFIG_USBHOST_HUB +static int sam_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected); +#endif static void sam_disconnect(FAR struct usbhost_driver_s *drvr); /* Initialization **************************************************************/ @@ -2687,12 +2698,12 @@ static inline void sam_portsc_bottomhalf(void) /* Are we bound to a class instance? */ - if (rhport->class) + if (rhport->hport.devclass) { /* Yes.. Disconnect the class */ - CLASS_DISCONNECTED(rhport->class); - rhport->class = NULL; + CLASS_DISCONNECTED(rhport->hport.devclass); + rhport->hport.devclass = NULL; } /* Notify any waiters for the Root Hub Status change @@ -2986,23 +2997,20 @@ static int sam_uhphs_interrupt(int irq, FAR void *context) * Name: sam_wait * * Description: - * Wait for a device to be connected or disconnected to/from a root hub port. + * Wait for a device to be connected or disconnected to/from a hub port. * * Input Parameters: * conn - The USB host connection instance obtained as a parameter from the call to * the USB driver initialization logic. - * connected - A pointer to an array of 3 boolean values corresponding to - * root hubs 1, 2, and 3. For each boolean value: TRUE: Wait for a device - * to be connected on the root hub; FALSE: wait for device to be - * disconnected from the root hub. + * hport - The location to return the hub port descriptor that detected the + * connection related event. * * Returned Values: - * And index [0, 1, or 2} corresponding to the root hub port number {1, 2, - * or 3} is returned when a device is connected or disconnected. This - * function will not return until either (1) a device is connected or - * disconnected to/from any root hub port or until (2) some failure occurs. - * On a failure, a negated errno value is returned indicating the nature of - * the failure + * Zero (OK) is returned on success when a device in connected or + * disconnected. This function will not return until either (1) a device is + * connected or disconnect to/from any hub port or until (2) some failure + * occurs. On a failure, a negated errno value is returned indicating the + * nature of the failure * * Assumptions: * - Called from a single thread so no mutual exclusion is required. @@ -3011,7 +3019,7 @@ static int sam_uhphs_interrupt(int irq, FAR void *context) *******************************************************************************/ static int sam_wait(FAR struct usbhost_connection_s *conn, - FAR const bool *connected) + FAR struct usbhost_hubport_s **hport) { irqstate_t flags; int rhpndx; @@ -3029,19 +3037,43 @@ static int sam_wait(FAR struct usbhost_connection_s *conn, { /* Has the connection state changed on the RH port? */ - if (g_ehci.rhport[rhpndx].connected != connected[rhpndx]) + if (g_ehci.rhport[rhpndx].hport.connected != connected[rhpndx]) { - /* Yes.. Return the RH port number to inform the caller which + /* Yes.. Return the RH port to inform the caller which * port has the connection change. */ + *hport = &g_ehci.rhport[rhpndx].hport; irqrestore(flags); + usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, - rhpndx + 1, g_ehci.rhport[rhpndx].connected); - return rhpndx; + rhpndx + 1, + g_ehci.rhport[rhpndx].hport.connected); + return OK; } } +#ifdef CONFIG_USBHOST_HUB + /* Is a device connected to an external hub? */ + + if (g_ehci.hport) + { + FAR volatile struct usbhost_hubport_s *connport; + + /* Yes.. return the external hub port */ + + connport = g_ehci.hport; + g_ehci.hport = NULL; + + *hport = connport; + irqrestore(flags); + + usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, + connport->port + 1, connport->connected); + return OK; + } +#endif + /* No changes on any port. Wait for a connection/disconnection event * and check again */ @@ -3060,33 +3092,37 @@ static int sam_wait(FAR struct usbhost_connection_s *conn, * extract the class ID info from the configuration descriptor, (3) call * usbhost_findclass() to find the class that supports this device, (4) * call the create() method on the struct usbhost_registry_s interface - * to get a class instance, and finally (5) call the configdesc() method + * to get a class instance, and finally (5) call the connect() method * of the struct usbhost_class_s interface. After that, the class is in * charge of the sequence of operations. * * Input Parameters: - * conn - The USB host connection instance obtained as a parameter from the call to - * the USB driver initialization logic. - * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n. + * conn - The USB host connection instance obtained as a parameter from + * the call to the USB driver initialization logic. + * hport - The descriptor of the hub port that has the newly connected + * device. * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is * returned indicating the nature of the failure * * Assumptions: - * - Only a single class bound to a single device is supported. - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. + * This function will *not* be called from an interrupt handler. * *******************************************************************************/ -static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) +static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) { struct sam_rhport_s *rhport; volatile uint32_t *regaddr; uint32_t regval; + int rhpndx; int ret; + DEBUGASSERT(conn != NULL && hport != NULL); + rhpndx = hport->port; + DEBUGASSERT(rhpndx >= 0 && rhpndx < SAM_EHCI_NRHPORT); rhport = &g_ehci.rhport[rhpndx]; @@ -3296,16 +3332,35 @@ static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) return -EPERM; } - /* Let the common usbhost_enumerate do all of the real work. Note that the - * FunctionAddress (USB address) is set to the root hub port number + 1 - * for now. - * - * REVISIT: Hub support will require better device address assignment. - * See include/nuttx/usb/usbhost_devaddr.h. + return OK; +} + +static int sam_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + int ret; + + /* If this is a connection on the root hub, then we need to go to + * little more effort to get the device speed. If it is a connection + * on an external hub, then we already have that information. */ - usbhost_vtrace2(EHCI_VTRACE2_CLASSENUM, rhpndx+1, rhpndx+1); - ret = usbhost_enumerate(&g_ehci.rhport[rhpndx].hport.hport, &rhport->class); + DEBUGASSERT(hport); +#ifdef CONFIG_USBHOST_HUB + if (ROOTHUB(hport)) +#endif + { + ret = sam_rh_enumerate(conn, hport); + if (ret < 0) + { + return ret; + } + } + + /* Then let the common usbhost_enumerate do the real enumeration. */ + + usbhost_vtrace1(EHCI_VTRACE2_CLASSENUM, hport->port); + ret = usbhost_enumerate(hport, &hport->devclass); if (ret < 0) { usbhost_trace2(EHCI_TRACE2_CLASSENUM_FAILED, rhpndx+1, -ret); @@ -3810,6 +3865,56 @@ static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, } #endif +/************************************************************************************ + * Name: sam_connect + * + * Description: + * New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * hport - The descriptor of the hub port that detected the connection + * related event + * connected - True: device connected; false: device disconnected + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_HUB +static int sam_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected) +{ + irqstate_t flags; + + /* Set the connected/disconnected flag */ + + hport->connected = connected; + ullvdbg("Hub port %d connected: %s\n", hport->port, connected ? "YES" : "NO"); + + /* Report the connection event */ + + flags = irqsave(); + DEBUGASSERT(g_ehci.hport == NULL); /* REVISIT */ + + g_ehci.hport = hport; + if (g_ehci.pscwait) + { + g_ehci.pscwait = false; + sam_givesem(&g_ehci.pscsem); + } + + irqrestore(flags); + return OK; +} +#endif + /******************************************************************************* * Name: sam_disconnect * @@ -3841,7 +3946,7 @@ static void sam_disconnect(FAR struct usbhost_driver_s *drvr) /* Unbind the class */ /* REVISIT: Is there more that needs to be done? */ - rhport->class = NULL; + rhport->hport.devclass = NULL; } /******************************************************************************* @@ -4111,6 +4216,9 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller) rhport->drvr.transfer = sam_transfer; #ifdef CONFIG_USBHOST_ASYNCH rhport->drvr.asynch = sam_asynch; +#endif +#ifdef CONFIG_USBHOST_HUB + rhport->drvr.connect = sam_connect; #endif rhport->drvr.disconnect = sam_disconnect; diff --git a/arch/arm/src/sama5/sam_ohci.c b/arch/arm/src/sama5/sam_ohci.c index 93d528994ef..cea1aea09c3 100644 --- a/arch/arm/src/sama5/sam_ohci.c +++ b/arch/arm/src/sama5/sam_ohci.c @@ -235,26 +235,29 @@ struct sam_rhport_s /* This is the hub port description understood by class drivers */ struct usbhost_roothubport_s hport; - - /* The bound device class driver */ - - struct usbhost_class_s *class; }; /* This structure retains the overall state of the USB host controller */ struct sam_ohci_s { - volatile bool rhswait; /* TRUE: Thread is waiting for Root Hub Status change */ + volatile bool pscwait; /* TRUE: Thread is waiting for Root Hub Status change */ #ifndef CONFIG_USBHOST_INT_DISABLE uint8_t ininterval; /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */ uint8_t outinterval; /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */ #endif + sem_t exclsem; /* Support mutually exclusive access */ - sem_t rhssem; /* Semaphore to wait Writeback Done Head event */ + sem_t pscsem; /* Semaphore to wait Writeback Done Head event */ struct work_s work; /* Supports interrupt bottom half */ +#ifdef CONFIG_USBHOST_HUB + /* Used to pass external hub port events */ + + volatile struct usbhost_hubport_s *hport; +#endif + /* Root hub ports */ struct sam_rhport_s rhport[SAM_OHCI_NRHPORT]; @@ -388,8 +391,11 @@ static void sam_ohci_bottomhalf(void *arg); /* USB host controller operations **********************************************/ static int sam_wait(FAR struct usbhost_connection_s *conn, - FAR const bool *connected); -static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx); + FAR struct usbhost_hubport_s **hport); +static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); +static int sam_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); static int sam_ep0configure(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, uint8_t funcaddr, uint16_t maxpacketsize); @@ -415,6 +421,11 @@ static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen, usbhost_asynch_t callback, FAR void *arg); #endif +#ifdef CONFIG_USBHOST_HUB +static int sam_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhsot_hubport_s *hport, + bool connected); +#endif static void sam_disconnect(FAR struct usbhost_driver_s *drvr); /******************************************************************************* @@ -1787,14 +1798,14 @@ static void sam_rhsc_bottomhalf(void) rhport->connected = true; usbhost_vtrace2(OHCI_VTRACE2_CONNECTED, - rhpndx + 1, g_ohci.rhswait); + rhpndx + 1, g_ohci.pscwait); /* Notify any waiters */ - if (g_ohci.rhswait) + if (g_ohci.pscwait) { - sam_givesem(&g_ohci.rhssem); - g_ohci.rhswait = false; + sam_givesem(&g_ohci.pscsem); + g_ohci.pscwait = false; } } else @@ -1825,29 +1836,29 @@ static void sam_rhsc_bottomhalf(void) /* Yes.. disconnect the device */ usbhost_vtrace2(OHCI_VTRACE2_DISCONNECTED, - rhpndx + 1, g_ohci.rhswait); + rhpndx + 1, g_ohci.pscwait); rhport->connected = false; rhport->hport.hport.speed = USB_SPEED_FULL; /* Are we bound to a class instance? */ - if (rhport->class) + if (rhport->hport.devclass) { /* Yes.. Disconnect the class */ - CLASS_DISCONNECTED(rhport->class); - rhport->class = NULL; + CLASS_DISCONNECTED(rhport->hport.devclass); + rhport->hport.devclass = NULL; } /* Notify any waiters for the Root Hub Status change * event. */ - if (g_ohci.rhswait) + if (g_ohci.pscwait) { - sam_givesem(&g_ohci.rhssem); - g_ohci.rhswait = false; + sam_givesem(&g_ohci.pscsem); + g_ohci.pscwait = false; } } else @@ -2056,23 +2067,20 @@ static void sam_ohci_bottomhalf(void *arg) * Name: sam_wait * * Description: - * Wait for a device to be connected or disconnected to/from a root hub port. + * Wait for a device to be connected or disconnected to/from a hub port. * * Input Parameters: * conn - The USB host connection instance obtained as a parameter from the call to * the USB driver initialization logic. - * connected - A pointer to an array of 3 boolean values corresponding to - * root hubs 1, 2, and 3. For each boolean value: TRUE: Wait for a device - * to be connected on the root hub; FALSE: wait for device to be - * disconnected from the root hub. + * hport - The location to return the hub port descriptor that detected the + * connection related event. * * Returned Values: - * And index [0, 1, or 2} corresponding to the root hub port number {1, 2, - * or 3} is returned when a device is connected or disconnected. This - * function will not return until either (1) a device is connected or - * disconnected to/from any root hub port or until (2) some failure occurs. - * On a failure, a negated errno value is returned indicating the nature of - * the failure + * Zero (OK) is returned on success when a device in connected or + * disconnected. This function will not return until either (1) a device is + * connected or disconnect to/from any hub port or until (2) some failure + * occurs. On a failure, a negated errno value is returned indicating the + * nature of the failure * * Assumptions: * - Called from a single thread so no mutual exclusion is required. @@ -2081,7 +2089,7 @@ static void sam_ohci_bottomhalf(void *arg) *******************************************************************************/ static int sam_wait(FAR struct usbhost_connection_s *conn, - FAR const bool *connected) + FAR struct usbhost_hubport_s **hport) { irqstate_t flags; int rhpndx; @@ -2122,16 +2130,42 @@ static int sam_wait(FAR struct usbhost_connection_s *conn, irqrestore(flags); usbhost_vtrace2(OHCI_VTRACE2_WAKEUP, rhpndx + 1, g_ohci.rhport[rhpndx].connected); - return rhpndx; + + *hport = &g_ohci.rphort[rhpndx].hport; + return OK; } } +#ifdef CONFIG_USBHOST_HUB + /* Is a device connected to an external hub? */ + + if (g_ohci.hport) + { + FAR struct usbhost_hubport_s *connport; + + /* Yes.. return the external hub port */ + + connport = (FAR struct usbhost_hubport_s *)g_ohci.hport; + g_ohci.hport = NULL; + + *hport = connport; + irqrestore(flags); + + usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, + connport->port + 1, connport->connected); + return OK; + } +#endif + + /* No changes on any port. Wait for a connection/disconnection event + * and check again + */ /* No changes on any port. Wait for a connection/disconnection event * and check again */ - g_ohci.rhswait = true; - sam_takesem(&g_ohci.rhssem); + g_ohci.pscwait = true; + sam_takesem(&g_ohci.pscsem); } } @@ -2144,32 +2178,36 @@ static int sam_wait(FAR struct usbhost_connection_s *conn, * extract the class ID info from the configuration descriptor, (3) call * usbhost_findclass() to find the class that supports this device, (4) * call the create() method on the struct usbhost_registry_s interface - * to get a class instance, and finally (5) call the configdesc() method + * to get a class instance, and finally (5) call the connect() method * of the struct usbhost_class_s interface. After that, the class is in * charge of the sequence of operations. * * Input Parameters: - * conn - The USB host connection instance obtained as a parameter from the call to - * the USB driver initialization logic. - * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n. + * conn - The USB host connection instance obtained as a parameter from + * the call to the USB driver initialization logic. + * hport - The descriptor of the hub port that has the newly connected + * device. * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is * returned indicating the nature of the failure * * Assumptions: - * - Only a single class bound to a single device is supported. - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. + * This function will *not* be called from an interrupt handler. * *******************************************************************************/ -static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) +static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) { struct sam_rhport_s *rhport; uint32_t regaddr; + int rhpndx; int ret; + DEBUGASSERT(conn != NULL && hport != NULL); + rhpndx = hport->port; + DEBUGASSERT(rhpndx >= 0 && rhpndx < SAM_OHCI_NRHPORT); rhport = &g_ohci.rhport[rhpndx]; @@ -2221,13 +2259,36 @@ static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) sam_putreg(OHCI_RHPORTST_PRSC, regaddr); up_mdelay(200); + return OK; +} - /* Let the common usbhost_enumerate do all of the real work. Note that the - * FunctionAddress (USB address) is set to the root hub port number for now. +static int sam_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + int ret; + + DEBUGASSERT(hport); + + /* If this is a connection on the root hub, then we need to go to + * little more effort to get the device speed. If it is a connection + * on an external hub, then we already have that information. */ - usbhost_vtrace2(OHCI_VTRACE2_CLASSENUM, rhpndx+1, rhpndx+1); - ret = usbhost_enumerate(&g_ohci.rhport[rhpndx].hport.hport, &rhport->class); +#ifdef CONFIG_USBHOST_HUB + if (ROOTHUB(hport)) +#endif + { + ret = sam_rh_enumerate(conn, hport); + if (ret < 0) + { + return ret; + } + } + + /* Then let the common usbhost_enumerate do the real enumeration. */ + + usbhost_vtrace1(OHCI_VTRACE1_CLASSENUM, hport->port); + ret = usbhost_enumerate(hport, &hport->devclass); if (ret < 0) { usbhost_trace2(OHCI_TRACE2_CLASSENUM_FAILED, rhpndx+1, -ret); @@ -3047,6 +3108,56 @@ static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, } #endif +/************************************************************************************ + * Name: sam_connect + * + * Description: + * New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * hport - The descriptor of the hub port that detected the connection + * related event + * connected - True: device connected; false: device disconnected + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_HUB +static int sam_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhsot_hubport_s *hport, + bool connected) +{ + irqstate_t flags; + + /* Set the connected/disconnected flag */ + + hport->connected = connected; + ullvdbg("Hub port %d connected: %s\n", hport->port, connected ? "YES" : "NO"); + + /* Report the connection event */ + + flags = irqsave(); + DEBUGASSERT(g_ohci.hport == NULL); /* REVISIT */ + + g_ohci.hport = hport; + if (g_ohci.pscwait) + { + g_ohci.pscwait = false; + sam_givesem(&g_ohci.pscsem); + } + + irqrestore(flags); + return OK; +} +#endif + /******************************************************************************* * Name: sam_disconnect * @@ -3082,7 +3193,7 @@ static void sam_disconnect(FAR struct usbhost_driver_s *drvr) /* Unbind the class */ - rhport->class = NULL; + rhport->hport.devclass = NULL; } /******************************************************************************* @@ -3131,7 +3242,7 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) /* Initialize the state data structure */ - sem_init(&g_ohci.rhssem, 0, 0); + sem_init(&g_ohci.pscsem, 0, 0); sem_init(&g_ohci.exclsem, 0, 1); #ifndef CONFIG_USBHOST_INT_DISABLE @@ -3254,6 +3365,9 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) rhport->drvr.transfer = sam_transfer; #ifdef CONFIG_USBHOST_ASYNCH rhport->drvr.asynch = sam_asynch; +#endif +#ifdef CONFIG_USBHOST_HUB + rhport->drvr.connect = sam_connect; #endif rhport->drvr.disconnect = sam_disconnect; diff --git a/arch/arm/src/sama5/sam_usbhost.c b/arch/arm/src/sama5/sam_usbhost.c index 89a63bea688..fb04b4a5158 100644 --- a/arch/arm/src/sama5/sam_usbhost.c +++ b/arch/arm/src/sama5/sam_usbhost.c @@ -104,6 +104,7 @@ static const struct sam_usbhost_trace_s g_trace1[TRACE1_NSTRINGS] = TRENTRY(OHCI_VTRACE1_ALREADYDISCONN, TR_OHCI, TR_FMT1, "OHCI Already disconnected, RHPORTST: %06x\n"), TRENTRY(OHCI_VTRACE1_RHSC, TR_OHCI, TR_FMT1, "OHCI Root Hub Status Change. Pending: %06x\n"), TRENTRY(OHCI_VTRACE1_WDHINTR, TR_OHCI, TR_FMT1, "OHCI Writeback Done Head interrupt. Pending: %06x\n"), + TRENTRY(OHCI_VTRACE1_CLASSENUM, TR_OHCI, TR_FMT1, "OHCI Hub port%d: Enumerate device\n"), TRENTRY(OHCI_VTRACE1_ENUMDISCONN, TR_OHCI, TR_FMT1, "OHCI RHport%dNot connected\n"), TRENTRY(OHCI_VTRACE1_INITIALIZING, TR_OHCI, TR_FMT1, "OHCI Initializing Stack\n"), TRENTRY(OHCI_VTRACE1_INITIALIZED, TR_OHCI, TR_FMT1, "OHCI Initialized\n"), @@ -145,6 +146,7 @@ static const struct sam_usbhost_trace_s g_trace1[TRACE1_NSTRINGS] = TRENTRY(EHCI_VTRACE1_TOPHALF, TR_EHCI, TR_FMT1, "EHCI Interrupt: %06x\n"), TRENTRY(EHCI_VTRACE1_AAINTR, TR_EHCI, TR_FMT1, "EHCI Async Advance Interrupt\n"), TRENTRY(EHCI_VTRACE1_USBINTR, TR_EHCI, TR_FMT1, "EHCI USB Interrupt (USBINT) Interrupt: %06x\n"), + TRENTRY(EHCI_VTRACE1_CLASSENUM, TR_EHCI, TR_FMT1, "EHCI Hub port%d: Enumerate device\n"), TRENTRY(EHCI_VTRACE1_ENUM_DISCONN, TR_EHCI, TR_FMT1, "EHCI Enumeration not connected\n"), TRENTRY(EHCI_VTRACE1_INITIALIZING, TR_EHCI, TR_FMT1, "EHCI Initializing EHCI Stack\n"), TRENTRY(EHCI_VTRACE1_HCCPARAMS, TR_EHCI, TR_FMT1, "EHCI HCCPARAMS=%06x\n"), @@ -160,7 +162,7 @@ static const struct sam_usbhost_trace_s g_trace2[TRACE2_NSTRINGS] = TRENTRY(OHCI_TRACE2_WHDTDSTATUS, TR_OHCI, TR_FMT2, "OHCI ERROR: WHD Bad TD completion status: %d xfrtype: %d\n"), TRENTRY(OHCI_TRACE2_EP0ENQUEUE_FAILED, TR_OHCI, TR_FMT2, "OHCI ERROR: RHport%d Failed to enqueue EP0: %d\n"), TRENTRY(OHCI_TRACE2_EDENQUEUE_FAILED, TR_OHCI, TR_FMT2, "OHCI ERROR: Failed to queue ED for transfer type %d: %d\n"), - TRENTRY(OHCI_TRACE2_CLASSENUM_FAILED, TR_OHCI, TR_FMT2, "OHCI RHport%d usbhost_enumerate() failed: %d\n"), + TRENTRY(OHCI_TRACE2_CLASSENUM_FAILED, TR_OHCI, TR_FMT2, "OHCI Hub port%d usbhost_enumerate() failed: %d\n"), #ifdef HAVE_USBHOST_TRACE_VERBOSE TRENTRY(OHCI_VTRACE2_INTERVAL, TR_OHCI, TR_FMT2, "OHCI interval: %d->%d\n"), @@ -169,7 +171,6 @@ static const struct sam_usbhost_trace_s g_trace2[TRACE2_NSTRINGS] = TRENTRY(OHCI_VTRACE2_CONNECTED, TR_OHCI, TR_FMT2, "OHCI RHPort%d connected, rhswait: %d\n"), TRENTRY(OHCI_VTRACE2_DISCONNECTED, TR_OHCI, TR_FMT2, "OHCI RHPort%d disconnected, rhswait: %d\n"), TRENTRY(OHCI_VTRACE2_WAKEUP, TR_OHCI, TR_FMT2, "OHCI RHPort%d connected: %d\n"), - TRENTRY(OHCI_VTRACE2_CLASSENUM, TR_OHCI, TR_FMT2, "OHCI RHPort%d: Enumerate the device, devaddr=%02x\n"), TRENTRY(OHCI_VTRACE2_EP0CONFIGURE, TR_OHCI, TR_FMT2, "OHCI RHPort%d EP0 CTRL: %04x\n"), TRENTRY(OHCI_VTRACE2_EPALLOC, TR_OHCI, TR_FMT2, "OHCI EP%d CTRL: %04x\n"), TRENTRY(OHCI_VTRACE2_CTRLIN, TR_OHCI, TR_FMT2, "OHCI CTRLIN RHPort%d req: %02x\n"), @@ -182,7 +183,7 @@ static const struct sam_usbhost_trace_s g_trace2[TRACE2_NSTRINGS] = #ifdef CONFIG_SAMA5_EHCI TRENTRY(EHCI_TRACE2_EPSTALLED, TR_EHCI, TR_FMT2, "EHCI EP%d Stalled: TOKEN=%04x\n"), TRENTRY(EHCI_TRACE2_EPIOERROR, TR_EHCI, TR_FMT2, "EHCI ERROR: EP%d TOKEN=%04x\n"), - TRENTRY(EHCI_TRACE2_CLASSENUM_FAILED, TR_EHCI, TR_FMT2, "EHCI RHport%d usbhost_enumerate() failed: %d\n"), + TRENTRY(EHCI_TRACE2_CLASSENUM_FAILED, TR_EHCI, TR_FMT2, "EHCI Hub port%d usbhost_enumerate() failed: %d\n"), #ifdef HAVE_USBHOST_TRACE_VERBOSE TRENTRY(EHCI_VTRACE2_ASYNCXFR, TR_EHCI, TR_FMT2, "EHCI Async transfer EP%d buflen=%d\n"), @@ -191,8 +192,7 @@ static const struct sam_usbhost_trace_s g_trace2[TRACE2_NSTRINGS] = TRENTRY(EHCI_VTRACE2_PORTSC, TR_EHCI, TR_FMT2, "EHCI PORTSC%d: %04x\n"), TRENTRY(EHCI_VTRACE2_PORTSC_CONNECTED, TR_EHCI, TR_FMT2, "EHCI RHPort%d connected, pscwait: %d\n"), TRENTRY(EHCI_VTRACE2_PORTSC_DISCONND, TR_EHCI, TR_FMT2, "EHCI RHport%d disconnected, pscwait: %d\n"), - TRENTRY(EHCI_VTRACE2_MONWAKEUP, TR_EHCI, TR_FMT2, "EHCI RHPort%d connected: %d\n"), - TRENTRY(EHCI_VTRACE2_CLASSENUM, TR_EHCI, TR_FMT2, "EHCI RHPort%d: Enumerate the device, devaddr=%02x\n"), + TRENTRY(EHCI_VTRACE2_MONWAKEUP, TR_EHCI, TR_FMT2, "EHCI Hub port%d connected: %d\n"), TRENTRY(EHCI_VTRACE2_EPALLOC, TR_EHCI, TR_FMT2, "EHCI EPALLOC: EP%d TYPE=%d\n"), TRENTRY(EHCI_VTRACE2_CTRLINOUT, TR_EHCI, TR_FMT2, "EHCI CTRLIN/OUT: RHPort%d req: %02x\n"), TRENTRY(EHCI_VTRACE2_HCIVERSION, TR_EHCI, TR_FMT2, "EHCI HCIVERSION %x.%02x\n"), diff --git a/configs/olimex-lpc1766stk/src/lpc17_nsh.c b/configs/olimex-lpc1766stk/src/lpc17_nsh.c index d17933c6117..cdd7bccfa29 100644 --- a/configs/olimex-lpc1766stk/src/lpc17_nsh.c +++ b/configs/olimex-lpc1766stk/src/lpc17_nsh.c @@ -141,25 +141,23 @@ static struct usbhost_connection_s *g_usbconn; #ifdef NSH_HAVEUSBHOST static int nsh_waiter(int argc, char *argv[]) { - bool connected = false; + struct usbhost_hubport_s *hport; syslog(LOG_INFO, "nsh_waiter: Running\n"); for (;;) { /* Wait for the device to change state */ - DEBUGVERIFY(CONN_WAIT(g_usbconn, &connected)); - - connected = !connected; - syslog(LOG_INFO, "%s\n", connected ? "connected" : "disconnected"); + DEBUGVERIFY(CONN_WAIT(g_usbconn, &hport)); + syslog(LOG_INFO, "%s\n", hport->connected ? "connected" : "disconnected"); /* Did we just become connected? */ - if (connected) + if (hport->connected) { /* Yes.. enumerate the newly connected device */ - (void)CONN_ENUMERATE(g_usbconn, 0); + (void)CONN_ENUMERATE(g_usbconn, hport); } } diff --git a/drivers/usbhost/usbhost_hub.c b/drivers/usbhost/usbhost_hub.c index 165576b26c8..e5aaa8ccb46 100644 --- a/drivers/usbhost/usbhost_hub.c +++ b/drivers/usbhost/usbhost_hub.c @@ -106,8 +106,9 @@ struct usbhost_hubpriv_s struct work_s work; /* Used for deferred callback work */ usbhost_ep_t intin; /* Interrupt IN endpoint */ - struct usbhost_class_s *childclass[USBHUB_MAX_PORTS]; - /* Pointer to child devices */ + /* Hub port descriptors */ + + struct usbhost_hubport_s hport[USBHUB_MAX_PORTS]; }; /* This represents the hub class structure. It must be cast compatible @@ -126,12 +127,7 @@ struct usbhost_hubclass_s /* Memory allocation services */ -static inline FAR struct usbhost_hubport_s *usbhost_allochub( - FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_class_s *hubclass, uint8_t speed, - uint8_t port); -static inline void usbhost_freehub(FAR struct usbhost_hubport_s *hport); -static inline void usbhost_freeclass(FAR struct usbhost_class_s *devclass); +static inline void usbhost_class_free(FAR struct usbhost_class_s *devclass); /* Worker thread actions */ @@ -196,9 +192,44 @@ static struct usbhost_registry_s g_hub = ****************************************************************************/ /**************************************************************************** - * Name: usbhost_allochub + * Name: usbhost_hport_free * * Description: + * Free a hub resource previously allocated by usbhost_hport_initialize(). + * + * Input Parameters: + * hport - A reference to the hub port instance to be freed. + * + * Returned Values: + * None + * + ****************************************************************************/ + +static inline void usbhost_hport_free(FAR struct usbhost_hubport_s *hport) +{ + uvdbg("Freeing: %p\n", hport); + + /* Free endpoints */ + + if (hport->ep0 != NULL) + { + DRVR_EPFREE(hport->drvr, hport->ep0); + hport->ep0 = NULL; + } + + /* Free the function address */ + + usbhost_devaddr_destroy(hport); + hport->funcaddr = 0; + + DEBUGASSERT(hport->devclass == NULL); +} + +/**************************************************************************** + * Name: usbhost_hport_initialize + * + * Description: + * Initialize and configure an * This is really part of the logic that implements the create() method * of struct usbhost_registry_s. This function allocates memory for one * new class instance. @@ -214,92 +245,39 @@ static struct usbhost_registry_s g_hub = * ****************************************************************************/ -static inline FAR struct usbhost_hubport_s * - usbhost_allochub(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_class_s *hubclass, - uint8_t speed, uint8_t port) +static int usbhost_hport_initialize(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_class_s *hubclass, + uint8_t port, + FAR struct usbhost_hubport_s *child) { - FAR struct usbhost_hubport_s *child; - FAR struct usbhost_hubport_s *parent; + FAR struct usbhost_hubport_s *parent = hubclass->hport; + struct usbhost_epdesc_s epdesc; + int ret; - DEBUGASSERT(hubclass != NULL && hubclass->hport != NULL); - parent = hubclass->hport; + child->drvr = drvr; + child->parent = parent; + child->funcaddr = 0; + child->speed = USB_SPEED_FULL; - /* We are not executing from an interrupt handler so we can just call - * kmm_malloc() to get memory for the class instance. - */ + epdesc.hport = child; + epdesc.addr = 0; + epdesc.in = 0; + epdesc.xfrtype = USB_EP_ATTR_XFER_CONTROL; + epdesc.interval = 0; + epdesc.mxpacketsize = 8; - DEBUGASSERT(!up_interrupt_context()); - child = (FAR struct usbhost_hubport_s *) - kmm_malloc(sizeof(struct usbhost_hubport_s)); - - uvdbg("Allocated: %p\n", child); - - if (child != NULL) + ret = DRVR_EPALLOC(drvr, &epdesc, &child->ep0); + if (ret != OK) { - struct usbhost_epdesc_s epdesc; - int ret; - - child->drvr = drvr; - child->parent = parent; - child->funcaddr = 0; - child->speed = speed; - - epdesc.hport = child; - epdesc.addr = 0; - epdesc.in = 0; - epdesc.xfrtype = USB_EP_ATTR_XFER_CONTROL; - epdesc.interval = 0; - epdesc.mxpacketsize = 8; - - ret = DRVR_EPALLOC(drvr, &epdesc, &child->ep0); - if (ret != OK) - { - udbg("ERROR: Failed to allocate ep0: %d\n", ret); - usbhost_freehub(child); - child = NULL; - } + udbg("ERROR: Failed to allocate ep0: %d\n", ret); + usbhost_hport_free(child); } - return child; + return ret; } /**************************************************************************** - * Name: usbhost_freehub - * - * Description: - * Free a hub instance previously allocated by usbhost_allochub(). - * - * Input Parameters: - * hport - A reference to the hub port instance to be freed. - * - * Returned Values: - * None - * - ****************************************************************************/ - -static inline void usbhost_freehub(FAR struct usbhost_hubport_s *hport) -{ - if (hport && !ROOTHUB(hport)) - { - if (hport->ep0 != NULL) - { - DRVR_EPFREE(hport->drvr, hport->ep0); - hport->ep0 = NULL; - } - - usbhost_devaddr_destroy(hport); - hport->funcaddr = 0; - - /* Free the hport instance */ - - uvdbg("Freeing: %p\n", hport); - kmm_free(hport); - } -} - -/**************************************************************************** - * Name: usbhost_freeclass + * Name: usbhost_class_free * * Description: * Free a class instance previously allocated by usbhost_create(). @@ -312,18 +290,25 @@ static inline void usbhost_freehub(FAR struct usbhost_hubport_s *hport) * ****************************************************************************/ -static inline void usbhost_freeclass(FAR struct usbhost_class_s *devclass) +static inline void usbhost_class_free(FAR struct usbhost_class_s *devclass) { + FAR struct usbhost_hubport_s *hport; DEBUGASSERT(devclass != NULL); - /* Free the bound hub */ + uvdbg("Freeing: %p\n", devclass); + DEBUGASSERT(devclass != NULL && devclass->hport != NULL); + hport = devclass->hport; - usbhost_freehub(devclass->hport); + /* Detach the class from the hub port */ + + usbhost_devaddr_destroy(hport); + hport->funcaddr = 0; /* Free the class instance */ - uvdbg("Freeing: %p\n", devclass); + DEBUGASSERT(hport->devclass == devclass); kmm_free(devclass); + hport->devclass = NULL; } /**************************************************************************** @@ -347,37 +332,46 @@ static void usbhost_destroy(FAR void *arg) FAR struct usbhost_class_s *hubclass = (FAR struct usbhost_class_s *)arg; FAR struct usbhost_hubpriv_s *priv; FAR struct usbhost_hubport_s *hport; - int i; - - DEBUGASSERT(hubclass != NULL); - priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; + int port; uvdbg("crefs: %d\n", priv->crefs); + DEBUGASSERT(hubclass != NULL && hubclass->hport != NULL); + priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; + hport = hubclass->hport; + + /* Destroy the interrupt IN endpoint */ + if (priv->intin) { - DEBUGASSERT(hubclass->hport); - hport = hubclass->hport; DRVR_EPFREE(hport->drvr, priv->intin); + priv->intin = NULL; + } + + /* Release per-per resource */ + + for (port = 0; port < USBHUB_MAX_PORTS; port++) + { + /* Free any devices classes connect on this hub port */ + + hport = &priv->hport[port]; + if (hport->devclass != NULL) + { + CLASS_DISCONNECTED(hport->devclass); + } + + /* Free any resource use by the hub port */ + + usbhost_hport_free(hport); } /* Destroy the semaphores */ sem_destroy(&priv->exclsem); - /* Destroy allocated child classes */ + /* Free the hub class structure */ - for (i = 0; i < USBHUB_MAX_PORTS; i++) - { - if (priv->childclass[i] != NULL) - { - CLASS_DISCONNECTED(priv->childclass[i]); - } - } - - /* Free the allocated class structure */ - - usbhost_freeclass(hubclass); + usbhost_class_free(hubclass); } /**************************************************************************** @@ -735,7 +729,6 @@ static inline int usbhost_hubpwr(FAR struct usbhost_class_s *hubclass, bool on) static void usbhost_hubevent(FAR void *arg) { FAR struct usbhost_class_s *hubclass; - FAR struct usbhost_hubport_s *newhub; FAR struct usbhost_hubport_s *hport; FAR struct usbhost_hubpriv_s *priv; FAR struct usb_ctrlreq_s *ctrlreq; @@ -930,7 +923,7 @@ static void usbhost_hubevent(FAR void *arg) if (!(status & USBHUB_PORT_STAT_RESET) && (status & USBHUB_PORT_STAT_ENABLE)) { - uint8_t speed; + FAR struct usbhost_hubport_s *connport; if (change & USBHUB_PORT_STAT_CRESET) { @@ -943,34 +936,26 @@ static void usbhost_hubevent(FAR void *arg) (void)DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); } + connport = &hubclass->hport[port]; if (status & USBHUB_PORT_STAT_HIGH_SPEED) { - speed = USB_SPEED_HIGH; + connport->speed = USB_SPEED_HIGH; } else if (status & USBHUB_PORT_STAT_LOW_SPEED) { - speed = USB_SPEED_LOW; + connport->speed = USB_SPEED_LOW; } else { - speed = USB_SPEED_FULL; + connport->speed = USB_SPEED_FULL; } - /* Allocate new hub class and enumerate */ + /* Inform waiters that a new device has been connected */ - newhub = usbhost_allochub(hport->drvr, hubclass, speed, port); - if (newhub) + ret = DRVR_CONNECT(connport->drvr, connport, true); + if (ret < 0) { - udbg("ERROR: Failed to allocated class\n"); - uvdbg("enumerate port %d speed %d\n", port, speed); - - ret = usbhost_enumerate(newhub, &priv->childclass[port]); - if (ret != OK) - { - udbg("ERROR: Failed to enumerate port %d: %d\n", - port, ret); - usbhost_freehub(hport); - } + udbg("ERROR: DRVR_CONNECT failed: %d\n", ret); } } else @@ -1118,7 +1103,7 @@ static FAR struct usbhost_class_s * FAR struct usbhost_class_s *hubclass; FAR struct usbhost_hubpriv_s *priv; size_t maxlen; - int child; + int port; int ret; /* Allocate a USB host class instance */ @@ -1166,15 +1151,30 @@ static FAR struct usbhost_class_s * sem_init(&priv->exclsem, 0, 1); - /* Initialize child class pointers */ + /* Initialize per-port data */ - for (child = 0; child < USBHUB_MAX_PORTS; child++) + for (port = 0; port < USBHUB_MAX_PORTS; port++) { - priv->childclass[child] = NULL; + /* Initialize the hub port descriptor */ + + ret = usbhost_hport_initialize(hport->drvr, hubclass, port, + &hubclass->hport[port]); + if (ret < 0) + { + goto errout_with_hports; + } } return hubclass; +errout_with_hports: + for (port = 0; port < USBHUB_MAX_PORTS; port++) + { + /* Free resource used by hub port descriptor */ + + usbhost_hport_free(hport); + } + errout_with_ctrlreq: kmm_free(priv->ctrlreq); diff --git a/include/nuttx/usb/usbhost.h b/include/nuttx/usb/usbhost.h index 4e8a20c0a19..7103c3d4cdf 100644 --- a/include/nuttx/usb/usbhost.h +++ b/include/nuttx/usb/usbhost.h @@ -163,22 +163,20 @@ * Name: CONN_WAIT * * Description: - * Wait for a device to be connected or disconnected to/from a root hub port. + * Wait for a device to be connected or disconnected to/from a hub port. * * Input Parameters: * conn - The USB host connection instance obtained as a parameter from the call to * the USB driver initialization logic. - * connected - A pointer to an array of n boolean values corresponding to - * root hubs 1 through n. For each boolean value: TRUE: Wait for a device - * to be connected on the root hub; FALSE: wait for device to be - * disconnected from the root hub. + * hport - The location to return the hub port descriptor that detected the + * connection related event. * * Returned Values: - * And index [0..(n-1)} corresponding to the root hub port number {1..n} is - * returned when a device in connected or disconnected. This function will not - * return until either (1) a device is connected or disconnect to/from any - * root hub port or until (2) some failure occurs. On a failure, a negated - * errno value is returned indicating the nature of the failure + * Zero (OK) is returned on success when a device in connected or + * disconnected. This function will not return until either (1) a device is + * connected or disconnect to/from any hub port or until (2) some failure + * occurs. On a failure, a negated errno value is returned indicating the + * nature of the failure * * Assumptions: * - Called from a single thread so no mutual exclusion is required. @@ -186,7 +184,7 @@ * *******************************************************************************/ -#define CONN_WAIT(conn, connected) ((conn)->wait(conn,connected)) +#define CONN_WAIT(conn,hport) ((conn)->wait(conn,hport)) /************************************************************************************ * Name: CONN_ENUMERATE @@ -202,9 +200,10 @@ * charge of the sequence of operations. * * Input Parameters: - * conn - The USB host connection instance obtained as a parameter from the call to - * the USB driver initialization logic. - * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n. + * conn - The USB host connection instance obtained as a parameter from + * the call to the USB driver initialization logic. + * hport - The descriptor of the hub port that has the newly connected + * device. * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is @@ -510,8 +509,34 @@ ((drvr)->transfer(drvr,ep,buffer,buflen)) #ifdef CONFIG_USBHOST_ASYNCH -#define DRVR_ASYNCH(drvr,ep,buffer,buflen,callback,arg) \ - ((drvr)->asynch(drvr,ep,buffer,buflen,callback,arg)) +# define DRVR_ASYNCH(drvr,ep,buffer,buflen,callback,arg) \ + ((drvr)->asynch(drvr,ep,buffer,buflen,callback,arg)) +#endif + +/************************************************************************************ + * Name: DRVR_CONNECT + * + * Description: + * New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * hport - The descriptor of the hub port that detected the connection + * related event + * connected - True: device connected; false: device disconnected + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_HUB +# define DRVR_CONNECT(drvr,hport,connected) \ + ((drvr)->connect(drvr,hport,connected)) #endif /************************************************************************************ @@ -618,7 +643,9 @@ struct usbhost_hubport_s #ifdef CONFIG_USBHOST_HUB FAR struct usbhost_hubport_s *parent; /* Parent hub (NULL=root hub) */ #endif + FAR struct usbhost_class_s *devclass; /* The bound device class driver */ usbhost_ep_t ep0; /* Control endpoint, ep0 */ + bool connected; /* True: device connected; false: disconnected */ uint8_t port; /* Hub port index */ uint8_t funcaddr; /* Device function address */ uint8_t speed; /* Device speed */ @@ -689,9 +716,10 @@ struct usbhost_connection_s { /* Wait for a device to connect or disconnect. */ - int (*wait)(FAR struct usbhost_connection_s *conn, FAR const bool *connected); + int (*wait)(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s **hport); - /* Enumerate the device connected on a root hub port. As part of this + /* Enumerate the device connected on a hub port. As part of this * enumeration process, the driver will (1) get the device's configuration * descriptor, (2) extract the class ID info from the configuration * descriptor, (3) call usbhost_findclass() to find the class that supports @@ -701,7 +729,8 @@ struct usbhost_connection_s * in charge of the sequence of operations. */ - int (*enumerate)(FAR struct usbhost_connection_s *conn, int rhpndx); + int (*enumerate)(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); }; /* Callback type used with asynchronous transfers */ @@ -794,6 +823,17 @@ struct usbhost_driver_s usbhost_asynch_t callback, FAR void *arg); #endif +#ifdef CONFIG_USBHOST_HUB + /* New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + */ + + int (*connect)(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected); +#endif + /* Called by the class when an error occurs and driver has been disconnected. * The USB host driver should discard the handle to the class instance (it is * stale) and not attempt any further interaction with the class driver instance @@ -1010,4 +1050,3 @@ int usbhost_enumerate(FAR struct usbhost_hubport_s *hub, #endif #endif /* __INCLUDE_NUTTX_USB_USBHOST_H */ -