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 5189dd7074
commit dd6c69cc06
4 changed files with 505 additions and 152 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"),