SAMA5 OHCI: Implement asynchronous I/O needed for hub support
This commit is contained in:
parent
d1283484ac
commit
977c9a5d27
@ -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;
|
||||||
|
@ -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.
|
||||||
|
@ -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"),
|
||||||
|
@ -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 */
|
||||||
|
@ -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
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user