USB EHCI: Implement the async() method
This commit is contained in:
parent
430b0369d4
commit
d2f9b3e2dc
@ -246,6 +246,10 @@ struct lpc31_epinfo_s
|
||||
int result; /* The result of the transfer */
|
||||
uint32_t xfrd; /* On completion, will hold the number of bytes transferred */
|
||||
sem_t iocsem; /* Semaphore used to wait for transfer completion */
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
usbhost_asynch_t callback; /* Transfer complete callback */
|
||||
void *arg; /* Argument that accompanies the callback */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* This structure retains the state of one root hub port */
|
||||
@ -483,6 +487,12 @@ static int lpc31_intr_setup(struct lpc31_rhport_s *rhport,
|
||||
struct lpc31_epinfo_s *epinfo, uint8_t *buffer, size_t buflen);
|
||||
#endif
|
||||
static ssize_t lpc31_transfer_wait(struct lpc31_epinfo_s *epinfo);
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
static inline int lpc31_asynch_setup(struct lpc31_rhport_s *rhport,
|
||||
struct lpc31_epinfo_s *epinfo, usbhost_asynch_t callback,
|
||||
FAR void *arg);
|
||||
static void lpc31_asynch_completion(struct lpc31_epinfo_s *epinfo);
|
||||
#endif
|
||||
|
||||
/* Interrupt Handling **********************************************************/
|
||||
|
||||
@ -1533,22 +1543,29 @@ static int lpc31_ioc_setup(struct lpc31_rhport_s *rhport, struct lpc31_epinfo_s
|
||||
int ret = -ENODEV;
|
||||
|
||||
DEBUGASSERT(rhport && epinfo && !epinfo->iocwait);
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
DEBUGASSERT(epinfo->callback == NULL);
|
||||
#endif
|
||||
|
||||
/* Is the device still connected? */
|
||||
|
||||
flags = irqsave();
|
||||
if (rhport->connected)
|
||||
{
|
||||
/* Then set wdhwait to indicate that we expect to be informed when
|
||||
/* Then set iocwait to indicate that we expect to be informed when
|
||||
* either (1) the device is disconnected, or (2) the transfer
|
||||
* completed.
|
||||
*/
|
||||
|
||||
epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */
|
||||
epinfo->status = 0; /* No status yet */
|
||||
epinfo->xfrd = 0; /* Nothing transferred yet */
|
||||
epinfo->result = -EBUSY; /* Transfer in progress */
|
||||
ret = OK; /* We are good to go */
|
||||
epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */
|
||||
epinfo->status = 0; /* No status yet */
|
||||
epinfo->xfrd = 0; /* Nothing transferred yet */
|
||||
epinfo->result = -EBUSY; /* Transfer in progress */
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
epinfo->callback = NULL; /* No asynchronous callback */
|
||||
epinfo->arg = NULL;
|
||||
#endif
|
||||
ret = OK; /* We are good to go */
|
||||
}
|
||||
|
||||
irqrestore(flags);
|
||||
@ -2479,6 +2496,106 @@ static ssize_t lpc31_transfer_wait(struct lpc31_epinfo_s *epinfo)
|
||||
return epinfo->xfrd;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Name: lpc31_asynch_setup
|
||||
*
|
||||
* Description:
|
||||
* Setup to receive an asynchronous notification when a transfer completes.
|
||||
*
|
||||
* Input Parameters:
|
||||
* epinfo - The IN or OUT endpoint descriptor for the device endpoint on
|
||||
* which the transfer will be performed.
|
||||
* callback - The function to be called when the completes
|
||||
* arg - An arbitrary argument that will be provided with the callback.
|
||||
*
|
||||
* Returned Values:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* - Called from the interrupt level
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
static inline int lpc31_asynch_setup(struct lpc31_rhport_s *rhport,
|
||||
struct lpc31_epinfo_s *epinfo,
|
||||
usbhost_asynch_t callback, FAR void *arg)
|
||||
{
|
||||
irqstate_t flags;
|
||||
int ret = -ENODEV;
|
||||
|
||||
DEBUGASSERT(rhport && epinfo && !epinfo->iocwait &&
|
||||
epinfo->callback == NULL);
|
||||
|
||||
/* Is the device still connected? */
|
||||
|
||||
flags = irqsave();
|
||||
if (rhport->connected)
|
||||
{
|
||||
/* Then save callback information to used when either (1) the
|
||||
* device is disconnected, or (2) the transfer completes.
|
||||
*/
|
||||
|
||||
epinfo->iocwait = false; /* No synchronous wakeup */
|
||||
epinfo->status = 0; /* No status yet */
|
||||
epinfo->xfrd = 0; /* Nothing transferred yet */
|
||||
epinfo->result = -EBUSY; /* Transfer in progress */
|
||||
epinfo->callback = callback; /* Asynchronous callback */
|
||||
epinfo->arg = arg; /* Argument that accompanies the callback */
|
||||
ret = OK; /* We are good to go */
|
||||
}
|
||||
|
||||
irqrestore(flags);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Name: lpc31_asynch_completion
|
||||
*
|
||||
* Description:
|
||||
* This function is called at the interrupt level when an asynchronous
|
||||
* transfer completes. It performs the pending callback.
|
||||
*
|
||||
* Input Parameters:
|
||||
* epinfo - The IN or OUT endpoint descriptor for the device endpoint on
|
||||
* which the transfer was performed.
|
||||
*
|
||||
* Returned Values:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* - Called from the interrupt level
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
static void lpc31_asynch_completion(struct lpc31_epinfo_s *epinfo)
|
||||
{
|
||||
usbhost_asynch_t callback;
|
||||
void *arg;
|
||||
int result;
|
||||
|
||||
DEBUGASSERT(epinfo != NULL && epinfo->iocwait == false &&
|
||||
epinfo->callback != NULL);
|
||||
|
||||
/* Extract and reset the callback info */
|
||||
|
||||
callback = epinfo->callback;
|
||||
arg = epinfo->arg;
|
||||
result = epinfo->result;
|
||||
|
||||
epinfo->callback = NULL;
|
||||
epinfo->arg = NULL;
|
||||
epinfo->result = OK;
|
||||
epinfo->iocwait = false;
|
||||
|
||||
/* Then perform the callback */
|
||||
|
||||
callback(arg, result);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* EHCI Interrupt Handling
|
||||
*******************************************************************************/
|
||||
@ -2663,6 +2780,17 @@ static int lpc31_qh_ioccheck(struct lpc31_qh_s *qh, uint32_t **bp, void *arg)
|
||||
epinfo->iocwait = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
/* No.. Is there a pending asynchronous transfer? */
|
||||
|
||||
else if (epinfo->callback != NULL)
|
||||
{
|
||||
/* Yes.. perform the callback */
|
||||
|
||||
lpc31_asynch_completion(epinfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Then release this QH by returning it to the free list */
|
||||
|
||||
lpc31_qh_free(qh);
|
||||
@ -4077,8 +4205,68 @@ static int lpc31_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)
|
||||
{
|
||||
# error Not implemented
|
||||
return -ENOSYS;
|
||||
struct lpc31_rhport_s *rhport = (struct lpc31_rhport_s *)drvr;
|
||||
struct lpc31_epinfo_s *epinfo = (struct lpc31_epinfo_s *)ep;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
|
||||
|
||||
/* We must have exclusive access to the EHCI hardware and data structures. */
|
||||
|
||||
lpc31_takesem(&g_ehci.exclsem);
|
||||
|
||||
/* Set the request for the callback well BEFORE initiating the transfer. */
|
||||
|
||||
ret = lpc31_asynch_setup(rhport, epinfo, callback, arg);
|
||||
if (ret != OK)
|
||||
{
|
||||
usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
|
||||
goto errout_with_sem;
|
||||
}
|
||||
|
||||
/* Initiate the transfer */
|
||||
|
||||
switch (epinfo->xfrtype)
|
||||
{
|
||||
case USB_EP_ATTR_XFER_BULK:
|
||||
ret = lpc31_async_setup(rhport, epinfo, NULL, buffer, buflen);
|
||||
break;
|
||||
|
||||
#ifndef CONFIG_USBHOST_INT_DISABLE
|
||||
case USB_EP_ATTR_XFER_INT:
|
||||
ret = lpc31_intr_setup(rhport, epinfo, buffer, buflen);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_USBHOST_ISOC_DISABLE
|
||||
case USB_EP_ATTR_XFER_ISOC:
|
||||
# warning "Isochronous endpoint support not emplemented"
|
||||
#endif
|
||||
case USB_EP_ATTR_XFER_CONTROL:
|
||||
default:
|
||||
usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype);
|
||||
ret = -ENOSYS;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for errors in the setup of the transfer */
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_callback;
|
||||
}
|
||||
|
||||
/* The transfer is in progress */
|
||||
|
||||
lpc31_givesem(&g_ehci.exclsem);
|
||||
return OK;
|
||||
|
||||
errout_with_callback:
|
||||
epinfo->callback = NULL;
|
||||
epinfo->arg = NULL;
|
||||
errout_with_sem:
|
||||
lpc31_givesem(&g_ehci.exclsem);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_USBHOST_ASYNCH */
|
||||
|
||||
|
@ -214,6 +214,10 @@ struct sam_epinfo_s
|
||||
int result; /* The result of the transfer */
|
||||
uint32_t xfrd; /* On completion, will hold the number of bytes transferred */
|
||||
sem_t iocsem; /* Semaphore used to wait for transfer completion */
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
usbhost_asynch_t callback; /* Transfer complete callback */
|
||||
void *arg; /* Argument that accompanies the callback */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* This structure retains the state of one root hub port */
|
||||
@ -360,6 +364,12 @@ static int sam_intr_setup(struct sam_rhport_s *rhport,
|
||||
struct sam_epinfo_s *epinfo, uint8_t *buffer, size_t buflen);
|
||||
#endif
|
||||
static ssize_t sam_transfer_wait(struct sam_epinfo_s *epinfo);
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
static inline int sam_asynch_setup(struct sam_rhport_s *rhport,
|
||||
struct sam_epinfo_s *epinfo, usbhost_asynch_t callback,
|
||||
FAR void *arg);
|
||||
static void sam_asynch_completion(struct sam_epinfo_s *epinfo);
|
||||
#endif
|
||||
|
||||
/* Interrupt Handling **********************************************************/
|
||||
|
||||
@ -1348,22 +1358,29 @@ static int sam_ioc_setup(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinf
|
||||
int ret = -ENODEV;
|
||||
|
||||
DEBUGASSERT(rhport && epinfo && !epinfo->iocwait);
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
DEBUGASSERT(epinfo->callback == NULL);
|
||||
#endif
|
||||
|
||||
/* Is the device still connected? */
|
||||
|
||||
flags = irqsave();
|
||||
if (rhport->connected)
|
||||
{
|
||||
/* Then set wdhwait to indicate that we expect to be informed when
|
||||
/* Then set iocwait to indicate that we expect to be informed when
|
||||
* either (1) the device is disconnected, or (2) the transfer
|
||||
* completed.
|
||||
*/
|
||||
|
||||
epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */
|
||||
epinfo->status = 0; /* No status yet */
|
||||
epinfo->xfrd = 0; /* Nothing transferred yet */
|
||||
epinfo->result = -EBUSY; /* Transfer in progress */
|
||||
ret = OK; /* We are good to go */
|
||||
epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */
|
||||
epinfo->status = 0; /* No status yet */
|
||||
epinfo->xfrd = 0; /* Nothing transferred yet */
|
||||
epinfo->result = -EBUSY; /* Transfer in progress */
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
epinfo->callback = NULL; /* No asynchronous callback */
|
||||
epinfo->arg = NULL;
|
||||
#endif
|
||||
ret = OK; /* We are good to go */
|
||||
}
|
||||
|
||||
irqrestore(flags);
|
||||
@ -2298,6 +2315,106 @@ static ssize_t sam_transfer_wait(struct sam_epinfo_s *epinfo)
|
||||
return epinfo->xfrd;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Name: sam_asynch_setup
|
||||
*
|
||||
* Description:
|
||||
* Setup to receive an asynchronous notification when a transfer completes.
|
||||
*
|
||||
* Input Parameters:
|
||||
* epinfo - The IN or OUT endpoint descriptor for the device endpoint on
|
||||
* which the transfer will be performed.
|
||||
* callback - The function to be called when the completes
|
||||
* arg - An arbitrary argument that will be provided with the callback.
|
||||
*
|
||||
* Returned Values:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* - Called from the interrupt level
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
static inline int sam_asynch_setup(struct sam_rhport_s *rhport,
|
||||
struct sam_epinfo_s *epinfo,
|
||||
usbhost_asynch_t callback, FAR void *arg)
|
||||
{
|
||||
irqstate_t flags;
|
||||
int ret = -ENODEV;
|
||||
|
||||
DEBUGASSERT(rhport && epinfo && !epinfo->iocwait &&
|
||||
epinfo->callback == NULL);
|
||||
|
||||
/* Is the device still connected? */
|
||||
|
||||
flags = irqsave();
|
||||
if (rhport->connected)
|
||||
{
|
||||
/* Then save callback information to used when either (1) the
|
||||
* device is disconnected, or (2) the transfer completes.
|
||||
*/
|
||||
|
||||
epinfo->iocwait = false; /* No synchronous wakeup */
|
||||
epinfo->status = 0; /* No status yet */
|
||||
epinfo->xfrd = 0; /* Nothing transferred yet */
|
||||
epinfo->result = -EBUSY; /* Transfer in progress */
|
||||
epinfo->callback = callback; /* Asynchronous callback */
|
||||
epinfo->arg = arg; /* Argument that accompanies the callback */
|
||||
ret = OK; /* We are good to go */
|
||||
}
|
||||
|
||||
irqrestore(flags);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* 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:
|
||||
* epinfo - The IN or OUT endpoint descriptor for the device endpoint on
|
||||
* which the transfer was performed.
|
||||
*
|
||||
* Returned Values:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* - Called from the interrupt level
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
static void sam_asynch_completion(struct sam_epinfo_s *epinfo)
|
||||
{
|
||||
usbhost_asynch_t callback;
|
||||
void *arg;
|
||||
int result;
|
||||
|
||||
DEBUGASSERT(epinfo != NULL && epinfo->iocwait == false &&
|
||||
epinfo->callback != NULL);
|
||||
|
||||
/* Extract and reset the callback info */
|
||||
|
||||
callback = epinfo->callback;
|
||||
arg = epinfo->arg;
|
||||
result = epinfo->result;
|
||||
|
||||
epinfo->callback = NULL;
|
||||
epinfo->arg = NULL;
|
||||
epinfo->result = OK;
|
||||
epinfo->iocwait = false;
|
||||
|
||||
/* Then perform the callback */
|
||||
|
||||
callback(arg, result);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* EHCI Interrupt Handling
|
||||
*******************************************************************************/
|
||||
@ -2482,6 +2599,17 @@ static int sam_qh_ioccheck(struct sam_qh_s *qh, uint32_t **bp, void *arg)
|
||||
epinfo->iocwait = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USBHOST_ASYNCH
|
||||
/* No.. Is there a pending asynchronous transfer? */
|
||||
|
||||
else if (epinfo->callback != NULL)
|
||||
{
|
||||
/* Yes.. perform the callback */
|
||||
|
||||
sam_asynch_completion(epinfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Then release this QH by returning it to the free list */
|
||||
|
||||
sam_qh_free(qh);
|
||||
@ -3864,7 +3992,7 @@ errout_with_sem:
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Name: lpc17_asynch
|
||||
* Name: sam_asynch
|
||||
*
|
||||
* Description:
|
||||
* Process a request to handle a transfer descriptor. This method will
|
||||
@ -3903,8 +4031,68 @@ 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)
|
||||
{
|
||||
# error Not implemented
|
||||
return -ENOSYS;
|
||||
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
|
||||
struct sam_epinfo_s *epinfo = (struct sam_epinfo_s *)ep;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
|
||||
|
||||
/* We must have exclusive access to the EHCI hardware and data structures. */
|
||||
|
||||
sam_takesem(&g_ehci.exclsem);
|
||||
|
||||
/* Set the request for the callback well BEFORE initiating the transfer. */
|
||||
|
||||
ret = sam_asynch_setup(rhport, epinfo, callback, arg);
|
||||
if (ret != OK)
|
||||
{
|
||||
usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
|
||||
goto errout_with_sem;
|
||||
}
|
||||
|
||||
/* Initiate the transfer */
|
||||
|
||||
switch (epinfo->xfrtype)
|
||||
{
|
||||
case USB_EP_ATTR_XFER_BULK:
|
||||
ret = sam_async_setup(rhport, epinfo, NULL, buffer, buflen);
|
||||
break;
|
||||
|
||||
#ifndef CONFIG_USBHOST_INT_DISABLE
|
||||
case USB_EP_ATTR_XFER_INT:
|
||||
ret = sam_intr_setup(rhport, epinfo, buffer, buflen);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_USBHOST_ISOC_DISABLE
|
||||
case USB_EP_ATTR_XFER_ISOC:
|
||||
# warning "Isochronous endpoint support not emplemented"
|
||||
#endif
|
||||
case USB_EP_ATTR_XFER_CONTROL:
|
||||
default:
|
||||
usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype);
|
||||
ret = -ENOSYS;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for errors in the setup of the transfer */
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_callback;
|
||||
}
|
||||
|
||||
/* The transfer is in progress */
|
||||
|
||||
sam_givesem(&g_ehci.exclsem);
|
||||
return OK;
|
||||
|
||||
errout_with_callback:
|
||||
epinfo->callback = NULL;
|
||||
epinfo->arg = NULL;
|
||||
errout_with_sem:
|
||||
sam_givesem(&g_ehci.exclsem);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_USBHOST_ASYNCH */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user