USB hub: Change to connection interface so that applications can deal with external hubs

This commit is contained in:
Gregory Nutt 2015-04-22 12:28:19 -06:00
parent 8e9fd9b838
commit f7ec9b0831
7 changed files with 698 additions and 308 deletions

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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"),

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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 */