SAMA5 OHCI: Implement asynchronous I/O needed for hub support

This commit is contained in:
Gregory Nutt 2015-05-02 09:38:08 -06:00
parent d1283484ac
commit 977c9a5d27
5 changed files with 503 additions and 183 deletions

View File

@ -1464,6 +1464,9 @@ static int lpc17_wdhwait(struct lpc17_usbhost_s *priv, struct lpc17_ed_s *ed)
*/ */
ed->wdhwait = true; ed->wdhwait = true;
#ifdef CONFIG_USBHOST_ASYNCH
ed->asynch = NULL;
#endif
ret = OK; ret = OK;
} }
@ -2602,9 +2605,12 @@ static int lpc17_transfer_common(struct lpc17_usbhost_s *priv,
* TDs on the Bulk list. * TDs on the Bulk list.
*/ */
regval = lpc17_getreg(LPC17_USBHOST_CMDST); if (ed->xfrtype == USB_EP_ATTR_XFER_BULK)
regval |= OHCI_CMDST_BLF; {
lpc17_putreg(regval, LPC17_USBHOST_CMDST); regval = lpc17_getreg(LPC17_USBHOST_CMDST);
regval |= OHCI_CMDST_BLF;
lpc17_putreg(regval, LPC17_USBHOST_CMDST);
}
} }
return ret; return ret;
@ -2829,10 +2835,6 @@ static int lpc17_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
goto errout_with_buffers; goto errout_with_buffers;
} }
#ifdef CONFIG_USBHOST_ASYNCH
ed->asynch = NULL;
#endif
/* Set up the transfer */ /* Set up the transfer */
ret = lpc17_transfer_common(priv, ed, buffer, buflen); ret = lpc17_transfer_common(priv, ed, buffer, buflen);
@ -3140,6 +3142,8 @@ static int lpc17_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
lpc17_freeasynch(asynch); lpc17_freeasynch(asynch);
} }
/* Determine the return value */
ret = ed->wdhwait ? -EINVAL : OK; ret = ed->wdhwait ? -EINVAL : OK;
irqrestore(flags); irqrestore(flags);
return ret; return ret;

View File

@ -211,6 +211,12 @@ struct sam_eplist_s
{ {
volatile bool wdhwait; /* TRUE: Thread is waiting for WDH interrupt */ volatile bool wdhwait; /* TRUE: Thread is waiting for WDH interrupt */
sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */ sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */
#ifdef CONFIG_USBHOST_ASYNCH
usbhost_asynch_t callback; /* Transfer complete callback */
void *arg; /* Argument that accompanies the callback */
uint8_t *buffer; /* Buffer being transferred */
uint16_t buflen; /* Length of the buffer */
#endif
struct sam_ed_s *ed; /* Endpoint descriptor (ED) */ struct sam_ed_s *ed; /* Endpoint descriptor (ED) */
struct sam_gtd_s *tail; /* Tail transfer descriptor (TD) */ struct sam_gtd_s *tail; /* Tail transfer descriptor (TD) */
}; };
@ -368,11 +374,11 @@ static unsigned int sam_getinterval(uint8_t interval);
static void sam_setinttab(uint32_t value, unsigned int interval, unsigned int offset); static void sam_setinttab(uint32_t value, unsigned int interval, unsigned int offset);
#endif #endif
static inline int sam_addinted(const FAR struct usbhost_epdesc_s *epdesc, static inline int sam_addinted(const struct usbhost_epdesc_s *epdesc,
struct sam_ed_s *ed); struct sam_ed_s *ed);
static inline int sam_reminted(struct sam_ed_s *ed); static inline int sam_reminted(struct sam_ed_s *ed);
static inline int sam_addisoced(const FAR struct usbhost_epdesc_s *epdesc, static inline int sam_addisoced(const struct usbhost_epdesc_s *epdesc,
struct sam_ed_s *ed); struct sam_ed_s *ed);
static inline int sam_remisoced(struct sam_ed_s *ed); static inline int sam_remisoced(struct sam_ed_s *ed);
@ -384,6 +390,11 @@ static int sam_enqueuetd(struct sam_rhport_s *rhport, struct sam_eplist_s *eplis
static int sam_ep0enqueue(struct sam_rhport_s *rhport); static int sam_ep0enqueue(struct sam_rhport_s *rhport);
static void sam_ep0dequeue(struct sam_rhport_s *rhport); static void sam_ep0dequeue(struct sam_rhport_s *rhport);
static int sam_wdhwait(struct sam_rhport_s *rhport, struct sam_ed_s *ed); static int sam_wdhwait(struct sam_rhport_s *rhport, struct sam_ed_s *ed);
#ifdef CONFIG_USBHOST_ASYNCH
static int sam_wdhasynch(struct sam_rhport_s *rhport, struct sam_ed_s *ed,
usbhost_asynch_t callback, void *arg,
uint8_t *buffer, uint16_t buflen);
#endif
static int sam_ctrltd(struct sam_rhport_s *rhport, struct sam_eplist_s *ep0, static int sam_ctrltd(struct sam_rhport_s *rhport, struct sam_eplist_s *ep0,
uint32_t dirpid, uint8_t *buffer, size_t buflen); uint32_t dirpid, uint8_t *buffer, size_t buflen);
@ -395,45 +406,49 @@ static void sam_ohci_bottomhalf(void *arg);
/* USB host controller operations **********************************************/ /* USB host controller operations **********************************************/
static int sam_wait(FAR struct usbhost_connection_s *conn, static int sam_wait(struct usbhost_connection_s *conn,
FAR struct usbhost_hubport_s **hport); struct usbhost_hubport_s **hport);
static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn, static int sam_rh_enumerate(struct usbhost_connection_s *conn,
FAR struct usbhost_hubport_s *hport); struct usbhost_hubport_s *hport);
static int sam_enumerate(FAR struct usbhost_connection_s *conn, static int sam_enumerate(struct usbhost_connection_s *conn,
FAR struct usbhost_hubport_s *hport); struct usbhost_hubport_s *hport);
static int sam_ep0configure(FAR struct usbhost_driver_s *drvr, static int sam_ep0configure(struct usbhost_driver_s *drvr,
usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed, usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed,
uint16_t maxpacketsize); uint16_t maxpacketsize);
static int sam_epalloc(FAR struct usbhost_driver_s *drvr, static int sam_epalloc(struct usbhost_driver_s *drvr,
const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep); const struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep);
static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); static int sam_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep);
static int sam_alloc(FAR struct usbhost_driver_s *drvr, static int sam_alloc(struct usbhost_driver_s *drvr,
FAR uint8_t **buffer, FAR size_t *maxlen); uint8_t **buffer, size_t *maxlen);
static int sam_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); static int sam_free(struct usbhost_driver_s *drvr, uint8_t *buffer);
static int sam_ioalloc(FAR struct usbhost_driver_s *drvr, static int sam_ioalloc(struct usbhost_driver_s *drvr,
FAR uint8_t **buffer, size_t buflen); uint8_t **buffer, size_t buflen);
static int sam_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); static int sam_iofree(struct usbhost_driver_s *drvr, uint8_t *buffer);
static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
FAR const struct usb_ctrlreq_s *req, const struct usb_ctrlreq_s *req,
FAR uint8_t *buffer); uint8_t *buffer);
static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, static int sam_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
FAR const struct usb_ctrlreq_s *req, const struct usb_ctrlreq_s *req,
FAR const uint8_t *buffer); const uint8_t *buffer);
static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, static int sam_transfer_common(struct sam_rhport_s *rhport,
FAR uint8_t *buffer, size_t buflen); struct sam_eplist_s *eplist,
uint8_t *buffer, size_t buflen);
static int sam_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
uint8_t *buffer, size_t buflen);
#ifdef CONFIG_USBHOST_ASYNCH #ifdef CONFIG_USBHOST_ASYNCH
static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, static void sam_asynch_completion(struct sam_eplist_s *eplist);
FAR uint8_t *buffer, size_t buflen, static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
usbhost_asynch_t callback, FAR void *arg); uint8_t *buffer, size_t buflen,
static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); usbhost_asynch_t callback, void *arg);
static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep);
#endif #endif
#ifdef CONFIG_USBHOST_HUB #ifdef CONFIG_USBHOST_HUB
static int sam_connect(FAR struct usbhost_driver_s *drvr, static int sam_connect(struct usbhost_driver_s *drvr,
FAR struct usbhost_hubport_s *hport, struct usbhost_hubport_s *hport,
bool connected); bool connected);
#endif #endif
static void sam_disconnect(FAR struct usbhost_driver_s *drvr); static void sam_disconnect(struct usbhost_driver_s *drvr);
/******************************************************************************* /*******************************************************************************
* Private Data * Private Data
@ -1125,7 +1140,7 @@ static void sam_setinttab(uint32_t value, unsigned int interval, unsigned int of
* *
*******************************************************************************/ *******************************************************************************/
static inline int sam_addinted(const FAR struct usbhost_epdesc_s *epdesc, static inline int sam_addinted(const struct usbhost_epdesc_s *epdesc,
struct sam_ed_s *ed) struct sam_ed_s *ed)
{ {
#ifndef CONFIG_USBHOST_INT_DISABLE #ifndef CONFIG_USBHOST_INT_DISABLE
@ -1387,7 +1402,7 @@ static inline int sam_reminted(struct sam_ed_s *ed)
* *
*******************************************************************************/ *******************************************************************************/
static inline int sam_addisoced(const FAR struct usbhost_epdesc_s *epdesc, static inline int sam_addisoced(const struct usbhost_epdesc_s *epdesc,
struct sam_ed_s *ed) struct sam_ed_s *ed)
{ {
#ifndef CONFIG_USBHOST_ISOC_DISABLE #ifndef CONFIG_USBHOST_ISOC_DISABLE
@ -1702,9 +1717,9 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport)
* *
* Description: * Description:
* Set the request for the Writeback Done Head event well BEFORE enabling the * Set the request for the Writeback Done Head event well BEFORE enabling the
* transfer (as soon as we are absolutely committed to the to avoid transfer). * transfer (as soon as we are absolutely committed to perform the transfer).
* We do this to minimize race conditions. This logic would have to be expanded * We do this to minimize race conditions. This logic would have to be
* if we want to have more than one packet in flight at a time! * expanded if we want to have more than one packet in flight at a time!
* *
*******************************************************************************/ *******************************************************************************/
@ -1728,14 +1743,67 @@ static int sam_wdhwait(struct sam_rhport_s *rhport, struct sam_ed_s *ed)
* completed. * completed.
*/ */
eplist->wdhwait = true; eplist->wdhwait = true;
ret = OK; #ifdef CONFIG_USBHOST_ASYNCH
eplist->callback = NULL;
eplist->arg = NULL;
eplist->buffer = NULL;
eplist->buflen = 0;
#endif
ret = OK;
} }
irqrestore(flags); irqrestore(flags);
return ret; return ret;
} }
/*******************************************************************************
* Name: sam_wdhasynch
*
* Description:
* Set the request for the Writeback Done Head callback well BEFORE enabling
* the transfer (as soon as we are absolutely committed to perform the
* transfer). We do this to minimize race conditions. This logic would have
* to be expanded if we want to have more than one packet in flight at a time!
*
*******************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH
static int sam_wdhasynch(struct sam_rhport_s *rhport, struct sam_ed_s *ed,
usbhost_asynch_t callback, void *arg,
uint8_t *buffer, uint16_t buflen)
{
struct sam_eplist_s *eplist;
irqstate_t flags = irqsave();
int ret = -ENODEV;
/* Is the device still connected? */
if (rhport->connected)
{
/* Yes.. Get the endpoint list associated with the ED */
eplist = ed->eplist;
DEBUGASSERT(eplist);
/* Then set wdhwait to indicate that we expect to be informed when
* either (1) the device is disconnected, or (2) the transfer
* completed.
*/
eplist->wdhwait = false;
eplist->callback = callback;
eplist->arg = arg;
eplist->buffer = buffer;
eplist->buflen = buflen;
ret = OK;
}
irqrestore(flags);
return ret;
}
#endif
/******************************************************************************* /*******************************************************************************
* Name: sam_ctrltd * Name: sam_ctrltd
* *
@ -1816,7 +1884,7 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, struct sam_eplist_s *eplist,
sam_takesem(&eplist->wdhsem); sam_takesem(&eplist->wdhsem);
} }
/* Re-aquire the ECHI semaphore. The caller expects to be holding /* Re-acquire the ECHI semaphore. The caller expects to be holding
* this upon return. * this upon return.
*/ */
@ -2080,6 +2148,17 @@ static void sam_wdh_bottomhalf(void)
sam_givesem(&eplist->wdhsem); sam_givesem(&eplist->wdhsem);
eplist->wdhwait = false; eplist->wdhwait = false;
} }
#ifdef CONFIG_USBHOST_ASYNCH
/* No thread waiting. Is there a callback scheduled? */
else if (eplist->callback)
{
/* Yes.. perform the callback */
sam_asynch_completion(eplist);
}
#endif
} }
} }
@ -2189,8 +2268,8 @@ static void sam_ohci_bottomhalf(void *arg)
* *
*******************************************************************************/ *******************************************************************************/
static int sam_wait(FAR struct usbhost_connection_s *conn, static int sam_wait(struct usbhost_connection_s *conn,
FAR struct usbhost_hubport_s **hport) struct usbhost_hubport_s **hport)
{ {
irqstate_t flags; irqstate_t flags;
int rhpndx; int rhpndx;
@ -2247,11 +2326,11 @@ static int sam_wait(FAR struct usbhost_connection_s *conn,
if (g_ohci.hport) if (g_ohci.hport)
{ {
FAR struct usbhost_hubport_s *connport; struct usbhost_hubport_s *connport;
/* Yes.. return the external hub port */ /* Yes.. return the external hub port */
connport = (FAR struct usbhost_hubport_s *)g_ohci.hport; connport = (struct usbhost_hubport_s *)g_ohci.hport;
g_ohci.hport = NULL; g_ohci.hport = NULL;
*hport = connport; *hport = connport;
@ -2303,8 +2382,8 @@ static int sam_wait(FAR struct usbhost_connection_s *conn,
* *
*******************************************************************************/ *******************************************************************************/
static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn, static int sam_rh_enumerate(struct usbhost_connection_s *conn,
FAR struct usbhost_hubport_s *hport) struct usbhost_hubport_s *hport)
{ {
struct sam_rhport_s *rhport; struct sam_rhport_s *rhport;
uint32_t regaddr; uint32_t regaddr;
@ -2368,8 +2447,8 @@ static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn,
return OK; return OK;
} }
static int sam_enumerate(FAR struct usbhost_connection_s *conn, static int sam_enumerate(struct usbhost_connection_s *conn,
FAR struct usbhost_hubport_s *hport) struct usbhost_hubport_s *hport)
{ {
int ret; int ret;
@ -2493,8 +2572,8 @@ static int sam_ep0configure(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
* *
************************************************************************************/ ************************************************************************************/
static int sam_epalloc(FAR struct usbhost_driver_s *drvr, static int sam_epalloc(struct usbhost_driver_s *drvr,
const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep) const struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep)
{ {
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
struct sam_eplist_s *eplist; struct sam_eplist_s *eplist;
@ -2692,7 +2771,7 @@ errout:
* *
************************************************************************************/ ************************************************************************************/
static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) static int sam_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep)
{ {
#ifdef CONFIG_DEBUG #ifdef CONFIG_DEBUG
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
@ -2784,8 +2863,8 @@ static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
* *
*******************************************************************************/ *******************************************************************************/
static int sam_alloc(FAR struct usbhost_driver_s *drvr, static int sam_alloc(struct usbhost_driver_s *drvr,
FAR uint8_t **buffer, FAR size_t *maxlen) uint8_t **buffer, size_t *maxlen)
{ {
int ret = -ENOMEM; int ret = -ENOMEM;
DEBUGASSERT(drvr && buffer && maxlen); DEBUGASSERT(drvr && buffer && maxlen);
@ -2828,7 +2907,7 @@ static int sam_alloc(FAR struct usbhost_driver_s *drvr,
* *
*******************************************************************************/ *******************************************************************************/
static int sam_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) static int sam_free(struct usbhost_driver_s *drvr, uint8_t *buffer)
{ {
DEBUGASSERT(drvr && buffer); DEBUGASSERT(drvr && buffer);
@ -2867,7 +2946,7 @@ static int sam_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer)
* *
************************************************************************************/ ************************************************************************************/
static int sam_ioalloc(FAR struct usbhost_driver_s *drvr, FAR uint8_t **buffer, static int sam_ioalloc(struct usbhost_driver_s *drvr, uint8_t **buffer,
size_t buflen) size_t buflen)
{ {
DEBUGASSERT(drvr && buffer); DEBUGASSERT(drvr && buffer);
@ -2901,7 +2980,7 @@ static int sam_ioalloc(FAR struct usbhost_driver_s *drvr, FAR uint8_t **buffer,
* *
************************************************************************************/ ************************************************************************************/
static int sam_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) static int sam_iofree(struct usbhost_driver_s *drvr, uint8_t *buffer)
{ {
DEBUGASSERT(drvr && buffer); DEBUGASSERT(drvr && buffer);
@ -2947,9 +3026,9 @@ static int sam_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer)
* *
*******************************************************************************/ *******************************************************************************/
static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
FAR const struct usb_ctrlreq_s *req, const struct usb_ctrlreq_s *req,
FAR uint8_t *buffer) uint8_t *buffer)
{ {
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep0; struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep0;
@ -2996,9 +3075,9 @@ static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
return ret; return ret;
} }
static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, static int sam_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
FAR const struct usb_ctrlreq_s *req, const struct usb_ctrlreq_s *req,
FAR const uint8_t *buffer) const uint8_t *buffer)
{ {
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep0; struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep0;
@ -3041,6 +3120,90 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
return ret; return ret;
} }
/*******************************************************************************
* Name: sam_transfer_common
*
* Description:
* Initiate a request to handle a transfer descriptor. This method will
* enqueue the transfer request and return immediately
*
* Input Parameters:
* rhport - Internal driver root hub port state structure.
* eplist - The internal representation of the device endpoint on which
* to perform the transfer.
* buffer - A buffer containing the data to be sent (OUT endpoint) or received
* (IN endpoint). buffer must have been allocated using DRVR_ALLOC
* buflen - The length of the data to be sent or received.
*
* Returned Values:
* On success, zero (OK) is returned. 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.
* - Never called from an interrupt handler.
*
*******************************************************************************/
static int sam_transfer_common(struct sam_rhport_s *rhport,
struct sam_eplist_s *eplist,
uint8_t *buffer, size_t buflen)
{
struct sam_ed_s *ed;
uint32_t dirpid;
uint32_t regval;
bool in;
int ret;
ed = eplist->ed;
in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN;
#ifdef CONFIG_USBHOST_TRACE
usbhost_vtrace2(OHCI_VTRACE2_TRANSFER,
(ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT,
(uint16_t)buflen);
#else
uvdbg("EP%d %s toggle: %d maxpacket: %d buflen: %d\n",
(ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT,
in ? "IN" : "OUT",
(ed->hw.headp & ED_HEADP_C) != 0 ? 1 : 0,
(ed->hw.ctrl & ED_CONTROL_MPS_MASK) >> ED_CONTROL_MPS_SHIFT,
buflen);
#endif
/* Get the direction of the endpoint */
if (in)
{
dirpid = GTD_STATUS_DP_IN;
}
else
{
dirpid = GTD_STATUS_DP_OUT;
}
/* Then enqueue the transfer */
ed->tdstatus = TD_CC_NOERROR;
ret = sam_enqueuetd(rhport, eplist, ed, dirpid, GTD_STATUS_T_TOGGLE,
buffer, buflen);
if (ret == OK)
{
/* BulkListFilled. This bit is used to indicate whether there are any
* TDs on the Bulk list.
*/
if (ed->xfrtype == USB_EP_ATTR_XFER_BULK)
{
regval = sam_getreg(SAM_USBHOST_CMDST);
regval |= OHCI_CMDST_BLF;
sam_putreg(regval, SAM_USBHOST_CMDST);
}
}
return ret;
}
/******************************************************************************* /*******************************************************************************
* Name: sam_transfer * Name: sam_transfer
* *
@ -3078,35 +3241,19 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
* *
*******************************************************************************/ *******************************************************************************/
static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, static int sam_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
FAR uint8_t *buffer, size_t buflen) uint8_t *buffer, size_t buflen)
{ {
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep; struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep;
struct sam_ed_s *ed; struct sam_ed_s *ed;
uint32_t dirpid;
uint32_t regval;
bool in; bool in;
int ret; int ret;
DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail && DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail &&
buffer && buflen > 0); buffer && buflen > 0);
ed = eplist->ed; ed = eplist->ed;
in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN; in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN;
#ifdef CONFIG_USBHOST_TRACE
usbhost_vtrace2(OHCI_VTRACE2_TRANSFER,
(ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT,
(uint16_t)buflen);
#else
uvdbg("EP%d %s toggle: %d maxpacket: %d buflen: %d\n",
(ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT,
in ? "IN" : "OUT",
(ed->hw.headp & ED_HEADP_C) != 0 ? 1 : 0,
(ed->hw.ctrl & ED_CONTROL_MPS_MASK) >> ED_CONTROL_MPS_SHIFT,
buflen);
#endif
/* We must have exclusive access to the endpoint, the TD pool, the I/O buffer /* We must have exclusive access to the endpoint, the TD pool, the I/O buffer
* pool, the bulk and interrupt lists, and the HCCA interrupt table. * pool, the bulk and interrupt lists, and the HCCA interrupt table.
@ -3125,89 +3272,68 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
goto errout; goto errout;
} }
/* Get the direction of the endpoint */ /* Set up the transfer */
if (in) ret = sam_transfer_common(rhport, eplist, buffer, buflen);
if (ret < 0)
{ {
dirpid = GTD_STATUS_DP_IN; udbg("ERROR: sam_transfer_common failed: %d\n", ret);
goto errout;
}
/* Release the OHCI semaphore while we wait. Other threads need the
* opportunity to access the OHCI resources while we wait.
*
* REVISIT: Is this safe? NO. This is a bug and needs rethinking.
* We need to lock all of the port-resources (not OHCI common) until
* the transfer is complete. But we can't use the common OHCI exclsem
* or we will deadlock while waiting (because the working thread that
* wakes this thread up needs the exclsem).
*/
#warning REVISIT
sam_givesem(&g_ohci.exclsem);
/* Wait for the Writeback Done Head interrupt Loop to handle any false
* alarm semaphore counts.
*/
while (eplist->wdhwait)
{
sam_takesem(&eplist->wdhsem);
}
/* Re-acquire the OCHI semaphore. The caller expects to be holding
* this upon return.
*/
sam_takesem(&g_ohci.exclsem);
/* Invalidate the D cache to force the ED to be reloaded from RAM */
arch_invalidate_dcache((uintptr_t)ed,
(uintptr_t)ed + sizeof(struct ohci_ed_s));
/* Check the TD completion status bits */
if (ed->tdstatus == TD_CC_NOERROR)
{
/* On an IN transaction, we also need to invalidate the buffer
* contents to force it to be reloaded from RAM.
*/
if (in)
{
arch_invalidate_dcache((uintptr_t)buffer,
(uintptr_t)buffer + buflen);
}
ret = OK;
} }
else else
{ {
dirpid = GTD_STATUS_DP_OUT; usbhost_trace2(OHCI_TRACE2_BADTDSTATUS, RHPORT(rhport),
} ed->tdstatus);
ret = ed->tdstatus == TD_CC_STALL ? -EPERM : -EIO;
/* Then enqueue the transfer */
ed->tdstatus = TD_CC_NOERROR;
ret = sam_enqueuetd(rhport, eplist, ed, dirpid, GTD_STATUS_T_TOGGLE,
buffer, buflen);
if (ret == OK)
{
/* BulkListFilled. This bit is used to indicate whether there are any
* TDs on the Bulk list.
*/
if (ed->xfrtype == USB_EP_ATTR_XFER_BULK)
{
regval = sam_getreg(SAM_USBHOST_CMDST);
regval |= OHCI_CMDST_BLF;
sam_putreg(regval, SAM_USBHOST_CMDST);
}
/* Release the OHCI semaphore while we wait. Other threads need the
* opportunity to access the OHCI resources while we wait.
*
* REVISIT: Is this safe? NO. This is a bug and needs rethinking.
* We need to lock all of the port-resources (not OHCI common) until
* the transfer is complete. But we can't use the common OHCI exclsem
* or we will deadlock while waiting (because the working thread that
* wakes this thread up needs the exclsem).
*/
#warning REVISIT
sam_givesem(&g_ohci.exclsem);
/* Wait for the Writeback Done Head interrupt Loop to handle any false
* alarm semaphore counts.
*/
while (eplist->wdhwait)
{
sam_takesem(&eplist->wdhsem);
}
/* Re-aquire the ECHI semaphore. The caller expects to be holding
* this upon return.
*/
sam_takesem(&g_ohci.exclsem);
/* Invalidate the D cache to force the ED to be reloaded from RAM */
arch_invalidate_dcache((uintptr_t)ed,
(uintptr_t)ed + sizeof(struct ohci_ed_s));
/* Check the TD completion status bits */
if (ed->tdstatus == TD_CC_NOERROR)
{
/* On an IN transaction, we also need to invalidate the buffer
* contents to force it to be reloaded from RAM.
*/
if (in)
{
arch_invalidate_dcache((uintptr_t)buffer,
(uintptr_t)buffer + buflen);
}
ret = OK;
}
else
{
usbhost_trace2(OHCI_TRACE2_BADTDSTATUS, RHPORT(rhport),
ed->tdstatus);
ret = ed->tdstatus == TD_CC_STALL ? -EPERM : -EIO;
}
} }
errout: errout:
@ -3219,7 +3345,85 @@ errout:
} }
/******************************************************************************* /*******************************************************************************
* Name: lcp17_asynch * Name: sam_asynch_completion
*
* Description:
* This function is called at the interrupt level when an asynchronous
* transfer completes. It performs the pending callback.
*
* Input Parameters:
* rhport - Internal driver root hub port state structure.
* eplist - The internal representation of the device endpoint on which
* to perform the transfer.
*
* Returned Values:
* None
*
* Assumptions:
* - Called from the interrupt level
*
*******************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH
static void sam_asynch_completion(struct sam_eplist_s *eplist)
{
struct sam_ed_s *ed;
usbhost_asynch_t callback;
void *arg;
int result;
DEBUGASSERT(eplist->ed && eplist->tail && eplist->callback != NULL &&
eplist->buffer != NULL && eplist->buflen > 0);
ed = eplist->ed;
/* Invalidate the D cache to force the ED to be reloaded from RAM */
arch_invalidate_dcache((uintptr_t)ed,
(uintptr_t)ed + sizeof(struct ohci_ed_s));
/* Check the TD completion status bits */
if (ed->tdstatus == TD_CC_NOERROR)
{
/* On an IN transaction, we also need to invalidate the buffer
* contents to force it to be reloaded from RAM.
*/
if ((ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN)
{
uintptr_t buffaddr = (uintptr_t)eplist->buffer;
arch_invalidate_dcache(buffaddr, buffaddr + eplist->buflen);
}
result = OK;
}
else
{
usbhost_trace1(OHCI_TRACE1_BADTDSTATUS, ed->tdstatus);
result = ed->tdstatus == TD_CC_STALL ? -EPERM : -EIO;
}
/* Extract the callback information before freeing the buffer */
callback = eplist->callback;
arg = eplist->arg;
/* Clear any pending transfer indicators */
eplist->wdhwait = false;
eplist->callback = NULL;
eplist->arg = NULL;
eplist->buffer = NULL;
eplist->buflen = 0;
/* Then perform the callback */
callback(arg, result);
}
#endif
/*******************************************************************************
* Name: sam_asynch
* *
* Description: * Description:
* Process a request to handle a transfer descriptor. This method will * Process a request to handle a transfer descriptor. This method will
@ -3254,12 +3458,59 @@ errout:
*******************************************************************************/ *******************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH #ifdef CONFIG_USBHOST_ASYNCH
static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
FAR uint8_t *buffer, size_t buflen, uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, FAR void *arg) usbhost_asynch_t callback, void *arg)
{ {
# error Not implemented struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
return -ENOSYS; struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep;
struct sam_ed_s *ed;
int ret;
DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail &&
buffer && buflen > 0 && buflen <= UINT16_MAX);
ed = eplist->ed;
/* We must have exclusive access to the endpoint, the TD pool, the I/O buffer
* pool, the bulk and interrupt lists, and the HCCA interrupt table.
*/
sam_takesem(&g_ohci.exclsem);
/* Set the request for the Writeback Done Head callback well BEFORE
* enabling thetransfer.
*/
ret = sam_wdhasynch(rhport, ed, callback, arg, buffer, buflen);
if (ret != OK)
{
usbhost_trace1(OHCI_TRACE1_DEVDISCONN, RHPORT(rhport));
goto errout;
}
/* Set up the transfer */
ret = sam_transfer_common(rhport, eplist, buffer, buflen);
if (ret < 0)
{
udbg("ERROR: sam_transfer_common failed: %d\n", ret);
goto errout;
}
/* Then just return. The callback will be performed asynchronously
* when the transfer completes.
*/
sam_givesem(&g_ohci.exclsem);
return OK;
errout:
/* Make sure that there is no outstanding request on this endpoint */
eplist->callback = NULL;
eplist->arg = NULL;
sam_givesem(&g_ohci.exclsem);
return ret;
} }
#endif /* CONFIG_USBHOST_ASYNCH */ #endif /* CONFIG_USBHOST_ASYNCH */
@ -3282,10 +3533,66 @@ static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
************************************************************************************/ ************************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH #ifdef CONFIG_USBHOST_ASYNCH
static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep)
{ {
# error Not implemented struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
return -ENOSYS; struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep;
struct sam_ed_s *ed;
struct sam_gtd_s *td;
struct sam_gtd_s *next;
uintptr_t paddr;
irqstate_t flags;
DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail);
ed = eplist->ed;
/* These first steps must be atomic as possible */
flags = irqsave();
/* It might be possible for no transfer to be in progress (callback == NULL),
* but it would be an usage error to use the interface to try to cancel a
* synchronous transfer (wdhwait == true).
*/
DEBUGASSERT(eplist->wdhwait == false);
if (eplist->wdhwait)
{
return -EINVAL;
}
if (eplist->callback)
{
/* We really need some kind of atomic test and set to do this right */
paddr = ed->hw.headp & ED_HEADP_ADDR_MASK;
td = (struct sam_gtd_s *)sam_virtramaddr(paddr);
paddr = sam_physramaddr((uintptr_t)eplist->tail);
ed->hw.headp = paddr;
/* Free all transfer descriptors that were connected to the ED */
DEBUGASSERT(td != (struct sam_gtd_s *)eplist->tail);
while (td != (struct sam_gtd_s *)eplist->tail)
{
paddr = (uintptr_t)td->hw.nexttd;
next = (struct sam_gtd_s *)sam_virtramaddr(paddr);
sam_tdfree(td);
td = next;
}
}
/* Reset any pending activity indications */
eplist->wdhwait = false;
eplist->callback = NULL;
eplist->arg = NULL;
eplist->buffer = NULL;
eplist->buflen = 0;
irqrestore(flags);
return OK;
} }
#endif /* CONFIG_USBHOST_ASYNCH */ #endif /* CONFIG_USBHOST_ASYNCH */
@ -3311,8 +3618,8 @@ static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
************************************************************************************/ ************************************************************************************/
#ifdef CONFIG_USBHOST_HUB #ifdef CONFIG_USBHOST_HUB
static int sam_connect(FAR struct usbhost_driver_s *drvr, static int sam_connect(struct usbhost_driver_s *drvr,
FAR struct usbhost_hubport_s *hport, struct usbhost_hubport_s *hport,
bool connected) bool connected)
{ {
irqstate_t flags; irqstate_t flags;
@ -3362,7 +3669,7 @@ static int sam_connect(FAR struct usbhost_driver_s *drvr,
* *
*******************************************************************************/ *******************************************************************************/
static void sam_disconnect(FAR struct usbhost_driver_s *drvr) static void sam_disconnect(struct usbhost_driver_s *drvr)
{ {
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
DEBUGASSERT(rhport); DEBUGASSERT(rhport);
@ -3406,9 +3713,9 @@ static void sam_disconnect(FAR struct usbhost_driver_s *drvr)
* *
*******************************************************************************/ *******************************************************************************/
FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) struct usbhost_connection_s *sam_ohci_initialize(int controller)
{ {
FAR struct usbhost_hubport_s *hport; struct usbhost_hubport_s *hport;
uintptr_t physaddr; uintptr_t physaddr;
uint32_t regval; uint32_t regval;
uint8_t *buffer; uint8_t *buffer;
@ -3685,7 +3992,7 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller)
* *
*******************************************************************************/ *******************************************************************************/
int sam_ohci_tophalf(int irq, FAR void *context) int sam_ohci_tophalf(int irq, void *context)
{ {
uint32_t intst; uint32_t intst;
uint32_t inten; uint32_t inten;
@ -3721,7 +4028,7 @@ int sam_ohci_tophalf(int irq, FAR void *context)
DEBUGASSERT(work_available(&g_ohci.work)); DEBUGASSERT(work_available(&g_ohci.work));
DEBUGVERIFY(work_queue(HPWORK, &g_ohci.work, sam_ohci_bottomhalf, DEBUGVERIFY(work_queue(HPWORK, &g_ohci.work, sam_ohci_bottomhalf,
(FAR void *)pending, 0)); (void *)pending, 0));
/* Disable further OHCI interrupts so that we do not overrun the /* Disable further OHCI interrupts so that we do not overrun the
* work queue. * work queue.

View File

@ -93,6 +93,9 @@ static const struct sam_usbhost_trace_s g_trace1[TRACE1_NSTRINGS] =
TRENTRY(OHCI_TRACE1_EDALLOC_FAILED, TR_OHCI, TR_FMT1, "OHCI ERROR: Failed to allocate ED\n"), TRENTRY(OHCI_TRACE1_EDALLOC_FAILED, TR_OHCI, TR_FMT1, "OHCI ERROR: Failed to allocate ED\n"),
TRENTRY(OHCI_TRACE1_TDALLOC_FAILED, TR_OHCI, TR_FMT1, "OHCI ERROR: Failed to allocate TD\n"), TRENTRY(OHCI_TRACE1_TDALLOC_FAILED, TR_OHCI, TR_FMT1, "OHCI ERROR: Failed to allocate TD\n"),
TRENTRY(OHCI_TRACE1_IRQATTACH, TR_OHCI, TR_FMT1, "OHCI ERROR: Failed to attach IRQ%d\n"), TRENTRY(OHCI_TRACE1_IRQATTACH, TR_OHCI, TR_FMT1, "OHCI ERROR: Failed to attach IRQ%d\n"),
#ifdef CONFIG_USBHOST_ASYNCH
TRENTRY(OHCI_TRACE1_BADTDSTATUS, TR_OHCI, TR_FMT1, "OHCI ERROR: Bad asynch TD completion status: %d\n"),
#endif
#ifdef HAVE_USBHOST_TRACE_VERBOSE #ifdef HAVE_USBHOST_TRACE_VERBOSE
TRENTRY(OHCI_VTRACE1_PHYSED, TR_OHCI, TR_FMT1, "OHCI physed: %06x\n"), TRENTRY(OHCI_VTRACE1_PHYSED, TR_OHCI, TR_FMT1, "OHCI physed: %06x\n"),

View File

@ -80,6 +80,9 @@ enum usbhost_trace1codes_e
OHCI_TRACE1_EDALLOC_FAILED, /* OHCI ERROR: Failed to allocate ED */ OHCI_TRACE1_EDALLOC_FAILED, /* OHCI ERROR: Failed to allocate ED */
OHCI_TRACE1_TDALLOC_FAILED, /* OHCI ERROR: Failed to allocate TD */ OHCI_TRACE1_TDALLOC_FAILED, /* OHCI ERROR: Failed to allocate TD */
OHCI_TRACE1_IRQATTACH, /* OHCI ERROR: Failed to attach IRQ */ OHCI_TRACE1_IRQATTACH, /* OHCI ERROR: Failed to attach IRQ */
#ifdef CONFIG_USBHOST_ASYNCH
OHCI_TRACE1_BADTDSTATUS, /* OHCI ERROR: Bad asynch TD completion status */
#endif
#ifdef HAVE_USBHOST_TRACE_VERBOSE #ifdef HAVE_USBHOST_TRACE_VERBOSE
OHCI_VTRACE1_PHYSED, /* OHCI physed */ OHCI_VTRACE1_PHYSED, /* OHCI physed */

View File

@ -1667,6 +1667,9 @@ USB High-Speed Host
2. Stack usage make increase when USB hub support is enabled because 2. Stack usage make increase when USB hub support is enabled because
the nesting depth of certain USB host class logic can increase. the nesting depth of certain USB host class logic can increase.
STATUS:
Hub support has not been verified on this board.
Mass Storage Device Usage Mass Storage Device Usage
------------------------- -------------------------