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

View File

@ -211,6 +211,12 @@ struct sam_eplist_s
{
volatile bool wdhwait; /* TRUE: Thread is waiting for WDH interrupt */
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_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);
#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);
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);
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 void sam_ep0dequeue(struct sam_rhport_s *rhport);
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,
uint32_t dirpid, uint8_t *buffer, size_t buflen);
@ -395,45 +406,49 @@ static void sam_ohci_bottomhalf(void *arg);
/* USB host controller operations **********************************************/
static int sam_wait(FAR struct usbhost_connection_s *conn,
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_wait(struct usbhost_connection_s *conn,
struct usbhost_hubport_s **hport);
static int sam_rh_enumerate(struct usbhost_connection_s *conn,
struct usbhost_hubport_s *hport);
static int sam_enumerate(struct usbhost_connection_s *conn,
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,
uint16_t maxpacketsize);
static int sam_epalloc(FAR struct usbhost_driver_s *drvr,
const FAR 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_alloc(FAR struct usbhost_driver_s *drvr,
FAR uint8_t **buffer, FAR size_t *maxlen);
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, size_t buflen);
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,
FAR const struct usb_ctrlreq_s *req,
FAR uint8_t *buffer);
static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
FAR const struct usb_ctrlreq_s *req,
FAR const uint8_t *buffer);
static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
FAR uint8_t *buffer, size_t buflen);
static int sam_epalloc(struct usbhost_driver_s *drvr,
const struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep);
static int sam_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep);
static int sam_alloc(struct usbhost_driver_s *drvr,
uint8_t **buffer, size_t *maxlen);
static int sam_free(struct usbhost_driver_s *drvr, uint8_t *buffer);
static int sam_ioalloc(struct usbhost_driver_s *drvr,
uint8_t **buffer, size_t buflen);
static int sam_iofree(struct usbhost_driver_s *drvr, uint8_t *buffer);
static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
uint8_t *buffer);
static int sam_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
const uint8_t *buffer);
static int sam_transfer_common(struct sam_rhport_s *rhport,
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
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);
static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep);
static void sam_asynch_completion(struct sam_eplist_s *eplist);
static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, void *arg);
static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep);
#endif
#ifdef CONFIG_USBHOST_HUB
static int sam_connect(FAR struct usbhost_driver_s *drvr,
FAR struct usbhost_hubport_s *hport,
static int sam_connect(struct usbhost_driver_s *drvr,
struct usbhost_hubport_s *hport,
bool connected);
#endif
static void sam_disconnect(FAR struct usbhost_driver_s *drvr);
static void sam_disconnect(struct usbhost_driver_s *drvr);
/*******************************************************************************
* 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)
{
#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)
{
#ifndef CONFIG_USBHOST_ISOC_DISABLE
@ -1702,9 +1717,9 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport)
*
* Description:
* 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).
* 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!
* 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!
*
*******************************************************************************/
@ -1728,14 +1743,67 @@ static int sam_wdhwait(struct sam_rhport_s *rhport, struct sam_ed_s *ed)
* completed.
*/
eplist->wdhwait = true;
ret = OK;
eplist->wdhwait = true;
#ifdef CONFIG_USBHOST_ASYNCH
eplist->callback = NULL;
eplist->arg = NULL;
eplist->buffer = NULL;
eplist->buflen = 0;
#endif
ret = OK;
}
irqrestore(flags);
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
*
@ -1816,7 +1884,7 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, struct sam_eplist_s *eplist,
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.
*/
@ -2080,6 +2148,17 @@ static void sam_wdh_bottomhalf(void)
sam_givesem(&eplist->wdhsem);
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,
FAR struct usbhost_hubport_s **hport)
static int sam_wait(struct usbhost_connection_s *conn,
struct usbhost_hubport_s **hport)
{
irqstate_t flags;
int rhpndx;
@ -2247,11 +2326,11 @@ static int sam_wait(FAR struct usbhost_connection_s *conn,
if (g_ohci.hport)
{
FAR struct usbhost_hubport_s *connport;
struct usbhost_hubport_s *connport;
/* 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;
*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,
FAR struct usbhost_hubport_s *hport)
static int sam_rh_enumerate(struct usbhost_connection_s *conn,
struct usbhost_hubport_s *hport)
{
struct sam_rhport_s *rhport;
uint32_t regaddr;
@ -2368,8 +2447,8 @@ static int sam_rh_enumerate(FAR struct usbhost_connection_s *conn,
return OK;
}
static int sam_enumerate(FAR struct usbhost_connection_s *conn,
FAR struct usbhost_hubport_s *hport)
static int sam_enumerate(struct usbhost_connection_s *conn,
struct usbhost_hubport_s *hport)
{
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,
const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep)
static int sam_epalloc(struct usbhost_driver_s *drvr,
const struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep)
{
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
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
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,
FAR uint8_t **buffer, FAR size_t *maxlen)
static int sam_alloc(struct usbhost_driver_s *drvr,
uint8_t **buffer, size_t *maxlen)
{
int ret = -ENOMEM;
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);
@ -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)
{
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);
@ -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,
FAR const struct usb_ctrlreq_s *req,
FAR uint8_t *buffer)
static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
uint8_t *buffer)
{
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
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;
}
static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
FAR const struct usb_ctrlreq_s *req,
FAR const uint8_t *buffer)
static int sam_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
const uint8_t *buffer)
{
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
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;
}
/*******************************************************************************
* 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
*
@ -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,
FAR 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)
{
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep;
struct sam_ed_s *ed;
uint32_t dirpid;
uint32_t regval;
bool in;
int ret;
DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail &&
buffer && buflen > 0);
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
in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN;
/* 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.
@ -3125,89 +3272,68 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
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
{
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);
}
/* 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;
}
usbhost_trace2(OHCI_TRACE2_BADTDSTATUS, RHPORT(rhport),
ed->tdstatus);
ret = ed->tdstatus == TD_CC_STALL ? -EPERM : -EIO;
}
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:
* Process a request to handle a transfer descriptor. This method will
@ -3254,12 +3458,59 @@ errout:
*******************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH
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)
static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, void *arg)
{
# error Not implemented
return -ENOSYS;
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
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 */
@ -3282,10 +3533,66 @@ static int sam_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
************************************************************************************/
#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
return -ENOSYS;
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
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 */
@ -3311,8 +3618,8 @@ static int sam_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
************************************************************************************/
#ifdef CONFIG_USBHOST_HUB
static int sam_connect(FAR struct usbhost_driver_s *drvr,
FAR struct usbhost_hubport_s *hport,
static int sam_connect(struct usbhost_driver_s *drvr,
struct usbhost_hubport_s *hport,
bool connected)
{
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;
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;
uint32_t regval;
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 inten;
@ -3721,7 +4028,7 @@ int sam_ohci_tophalf(int irq, FAR void *context)
DEBUGASSERT(work_available(&g_ohci.work));
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
* 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_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"),
#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
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_TDALLOC_FAILED, /* OHCI ERROR: Failed to allocate TD */
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
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
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
-------------------------