mirror of
https://github.com/apache/nuttx.git
synced 2026-05-30 05:16:47 +08:00
SAM4E UDP: Several fixes related to bulk endpoint transfers
This commit is contained in:
@@ -296,6 +296,7 @@ struct sam_ep_s
|
|||||||
|
|
||||||
struct sam_usbdev_s *dev; /* Reference to private driver data */
|
struct sam_usbdev_s *dev; /* Reference to private driver data */
|
||||||
struct sam_rqhead_s reqq; /* Read/write request queue */
|
struct sam_rqhead_s reqq; /* Read/write request queue */
|
||||||
|
struct sam_rqhead_s pendq; /* Write requests pending stall sent */
|
||||||
volatile uint8_t epstate; /* State of the endpoint (see enum sam_epstate_e) */
|
volatile uint8_t epstate; /* State of the endpoint (see enum sam_epstate_e) */
|
||||||
uint8_t stalled:1; /* true: Endpoint is stalled */
|
uint8_t stalled:1; /* true: Endpoint is stalled */
|
||||||
uint8_t pending:1; /* true: IN Endpoint stall is pending */
|
uint8_t pending:1; /* true: IN Endpoint stall is pending */
|
||||||
@@ -373,9 +374,6 @@ static struct sam_req_s *
|
|||||||
sam_req_dequeue(struct sam_rqhead_s *queue);
|
sam_req_dequeue(struct sam_rqhead_s *queue);
|
||||||
static void sam_req_enqueue(struct sam_rqhead_s *queue,
|
static void sam_req_enqueue(struct sam_rqhead_s *queue,
|
||||||
struct sam_req_s *req);
|
struct sam_req_s *req);
|
||||||
static inline void
|
|
||||||
sam_req_abort(struct sam_ep_s *privep,
|
|
||||||
struct sam_req_s *privreq, int16_t result);
|
|
||||||
static void sam_req_complete(struct sam_ep_s *privep, int16_t result);
|
static void sam_req_complete(struct sam_ep_s *privep, int16_t result);
|
||||||
static void sam_req_wrsetup(struct sam_usbdev_s *priv,
|
static void sam_req_wrsetup(struct sam_usbdev_s *priv,
|
||||||
struct sam_ep_s *privep, struct sam_req_s *privreq);
|
struct sam_ep_s *privep, struct sam_req_s *privreq);
|
||||||
@@ -792,25 +790,6 @@ static void sam_req_enqueue(struct sam_rqhead_s *queue, struct sam_req_s *req)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Name: sam_req_abort
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
sam_req_abort(struct sam_ep_s *privep, struct sam_req_s *privreq, int16_t result)
|
|
||||||
{
|
|
||||||
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_REQABORTED),
|
|
||||||
(uint16_t)USB_EPNO(privep->ep.eplog));
|
|
||||||
|
|
||||||
/* Save the result in the request structure */
|
|
||||||
|
|
||||||
privreq->req.result = result;
|
|
||||||
|
|
||||||
/* Callback to the request completion handler */
|
|
||||||
|
|
||||||
privreq->req.callback(&privep->ep, &privreq->req);
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: sam_req_complete
|
* Name: sam_req_complete
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@@ -861,7 +840,6 @@ static void sam_req_wrsetup(struct sam_usbdev_s *priv,
|
|||||||
volatile uint32_t *fifo;
|
volatile uint32_t *fifo;
|
||||||
uint8_t epno;
|
uint8_t epno;
|
||||||
int nbytes;
|
int nbytes;
|
||||||
int bytesleft;
|
|
||||||
|
|
||||||
/* Get the unadorned endpoint number */
|
/* Get the unadorned endpoint number */
|
||||||
|
|
||||||
@@ -873,17 +851,7 @@ static void sam_req_wrsetup(struct sam_usbdev_s *priv,
|
|||||||
|
|
||||||
/* Get the number of bytes remaining to be sent. */
|
/* Get the number of bytes remaining to be sent. */
|
||||||
|
|
||||||
bytesleft = privreq->req.len - privreq->req.xfrd;
|
nbytes = privreq->req.len - privreq->req.xfrd;
|
||||||
|
|
||||||
/* Clip the requested transfer size to the number of bytes actually
|
|
||||||
* available
|
|
||||||
*/
|
|
||||||
|
|
||||||
nbytes = bytesleft;
|
|
||||||
if (nbytes > bytesleft)
|
|
||||||
{
|
|
||||||
nbytes = bytesleft;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we are not sending a zero length packet, then clip the size to
|
/* If we are not sending a zero length packet, then clip the size to
|
||||||
* maxpacket and check if we need to send a following zero length packet.
|
* maxpacket and check if we need to send a following zero length packet.
|
||||||
@@ -897,7 +865,7 @@ static void sam_req_wrsetup(struct sam_usbdev_s *priv,
|
|||||||
|
|
||||||
if (nbytes >= privep->ep.maxpacket)
|
if (nbytes >= privep->ep.maxpacket)
|
||||||
{
|
{
|
||||||
nbytes = privep->ep.maxpacket;
|
nbytes = privep->ep.maxpacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is the new number of bytes "in-flight" */
|
/* This is the new number of bytes "in-flight" */
|
||||||
@@ -974,9 +942,10 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
|
|||||||
epno = USB_EPNO(privep->ep.eplog);
|
epno = USB_EPNO(privep->ep.eplog);
|
||||||
|
|
||||||
/* We get here when an IN endpoint interrupt occurs. So now we know that
|
/* We get here when an IN endpoint interrupt occurs. So now we know that
|
||||||
* there is no TX transfer in progress.
|
* there is no TX transfer in progress (epstate should be IDLE).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
DEBUGASSERT(privep->epstate == UDP_EPSTATE_IDLE);
|
||||||
while (privep->epstate == UDP_EPSTATE_IDLE)
|
while (privep->epstate == UDP_EPSTATE_IDLE)
|
||||||
{
|
{
|
||||||
/* Check the request from the head of the endpoint request queue */
|
/* Check the request from the head of the endpoint request queue */
|
||||||
@@ -990,10 +959,6 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
|
|||||||
|
|
||||||
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINQEMPTY), 0);
|
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINQEMPTY), 0);
|
||||||
|
|
||||||
/* Clear any pending the TXCOMP interrupt */
|
|
||||||
|
|
||||||
sam_csr_clrbits(epno, UDPEP_CSR_TXCOMP);
|
|
||||||
|
|
||||||
/* Was there a pending endpoint stall? */
|
/* Was there a pending endpoint stall? */
|
||||||
|
|
||||||
if (privep->pending)
|
if (privep->pending)
|
||||||
@@ -1040,7 +1005,7 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
|
|||||||
privep->zlpneeded = false;
|
privep->zlpneeded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform the write operation */
|
/* Perform the write operation. epstate will become SENDING. */
|
||||||
|
|
||||||
sam_req_wrsetup(priv, privep, privreq);
|
sam_req_wrsetup(priv, privep, privreq);
|
||||||
}
|
}
|
||||||
@@ -1080,27 +1045,16 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
|
|||||||
|
|
||||||
/* If all of the bytes were sent (including any final zero length
|
/* If all of the bytes were sent (including any final zero length
|
||||||
* packet) then we are finished with the request buffer), then we can
|
* packet) then we are finished with the request buffer), then we can
|
||||||
* return the request buffer to the class driver. The transfer is not
|
* return the request buffer to the class driver. The transfer may
|
||||||
* finished yet, however. There are still bytes in flight. The
|
* not finished yet, however. There may still be bytes in flight.
|
||||||
* transfer is truly finished when we are called again and the
|
* The transfer is truly finished when we are called again and the
|
||||||
* request buffer is empty.
|
* request buffer is empty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (privreq->req.len >= privreq->req.xfrd &&
|
if (privreq->req.len >= privreq->req.xfrd &&
|
||||||
privep->epstate == UDP_EPSTATE_IDLE)
|
privep->epstate == UDP_EPSTATE_IDLE)
|
||||||
{
|
{
|
||||||
/* TXCOMP must be cleared after the last packet has been set.
|
/* Return the write request to the class driver */
|
||||||
* TXCOMP was set by the USB device when it has received an ACK
|
|
||||||
* PID signal for the Data IN packet. An interrupt is pending
|
|
||||||
* while TXCOMP is set.
|
|
||||||
*
|
|
||||||
* REVISIT: This function might be called before TXCOMP is
|
|
||||||
* actually received.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//sam_csr_clrbits(epno, UDPEP_CSR_TXCOMP);
|
|
||||||
|
|
||||||
/* Return the write request to the class driver */
|
|
||||||
|
|
||||||
usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
|
usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
|
||||||
privreq->req.xfrd);
|
privreq->req.xfrd);
|
||||||
@@ -1970,6 +1924,10 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
|
|||||||
{
|
{
|
||||||
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_TXCOMP), (uint16_t)csr);
|
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_TXCOMP), (uint16_t)csr);
|
||||||
|
|
||||||
|
/* Clear the TXCOMP interrupt */
|
||||||
|
|
||||||
|
sam_csr_clrbits(epno, UDPEP_CSR_TXCOMP);
|
||||||
|
|
||||||
/* Sending state. This is the completion of a "normal" write request
|
/* Sending state. This is the completion of a "normal" write request
|
||||||
* transfer. In this case, we need to resume request processing in
|
* transfer. In this case, we need to resume request processing in
|
||||||
* order to send the next outgoing packet.
|
* order to send the next outgoing packet.
|
||||||
@@ -1978,9 +1936,7 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
|
|||||||
if (privep->epstate == UDP_EPSTATE_SENDING ||
|
if (privep->epstate == UDP_EPSTATE_SENDING ||
|
||||||
privep->epstate == UDP_EPSTATE_EP0STATUSIN)
|
privep->epstate == UDP_EPSTATE_EP0STATUSIN)
|
||||||
{
|
{
|
||||||
/* Continue/resume processing the write requests. TXCOMP will
|
/* Continue/resume processing the write requests */
|
||||||
* be cleared by sam_req_write().
|
|
||||||
*/
|
|
||||||
|
|
||||||
privep->epstate = UDP_EPSTATE_IDLE;
|
privep->epstate = UDP_EPSTATE_IDLE;
|
||||||
(void)sam_req_write(priv, privep);
|
(void)sam_req_write(priv, privep);
|
||||||
@@ -2002,19 +1958,12 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
|
|||||||
|
|
||||||
privep->epstate = UDP_EPSTATE_IDLE;
|
privep->epstate = UDP_EPSTATE_IDLE;
|
||||||
sam_setdevaddr(priv, priv->devaddr);
|
sam_setdevaddr(priv, priv->devaddr);
|
||||||
|
|
||||||
/* Acknowledge the TXCOMP interrupt */
|
|
||||||
|
|
||||||
sam_csr_clrbits(epno, UDPEP_CSR_TXCOMP);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Unexpected TXCOMP interrupt. Complain and acknowledge the
|
/* Unexpected TXCOMP interrupt */
|
||||||
* TXCOMP interrupt.
|
|
||||||
*/
|
|
||||||
|
|
||||||
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_TXCOMPERR), privep->epstate);
|
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_TXCOMPERR), privep->epstate);
|
||||||
sam_csr_clrbits(epno, UDPEP_CSR_TXCOMP);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2587,7 +2536,7 @@ static int sam_ep_stall(struct sam_ep_s *privep)
|
|||||||
|
|
||||||
sam_csr_setbits(epno, UDPEP_CSR_FORCESTALL);
|
sam_csr_setbits(epno, UDPEP_CSR_FORCESTALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
irqrestore(flags);
|
irqrestore(flags);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -2599,6 +2548,7 @@ static int sam_ep_stall(struct sam_ep_s *privep)
|
|||||||
static int sam_ep_resume(struct sam_ep_s *privep)
|
static int sam_ep_resume(struct sam_ep_s *privep)
|
||||||
{
|
{
|
||||||
struct sam_usbdev_s *priv;
|
struct sam_usbdev_s *priv;
|
||||||
|
struct sam_req_s *req;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
uint8_t epno;
|
uint8_t epno;
|
||||||
|
|
||||||
@@ -2623,18 +2573,24 @@ static int sam_ep_resume(struct sam_ep_s *privep)
|
|||||||
privep->pending = false;
|
privep->pending = false;
|
||||||
privep->epstate = UDP_EPSTATE_IDLE;
|
privep->epstate = UDP_EPSTATE_IDLE;
|
||||||
|
|
||||||
/* Clear FORCESTALL request
|
/* Clear FORCESTALL request */
|
||||||
* REVISIT: Data sheet says to reset toggle to DATA0 only on OUT
|
|
||||||
* endpoints.
|
|
||||||
*/
|
|
||||||
|
|
||||||
sam_csr_clrbits(epno, UDPEP_CSR_DTGLE | UDPEP_CSR_FORCESTALL);
|
sam_csr_clrbits(epno, UDPEP_CSR_FORCESTALL);
|
||||||
|
|
||||||
/* Reset the endpoint FIFO */
|
/* Reset the endpoint FIFO */
|
||||||
|
|
||||||
sam_putreg(UDP_RSTEP(epno), SAM_UDP_RSTEP);
|
sam_putreg(UDP_RSTEP(epno), SAM_UDP_RSTEP);
|
||||||
sam_putreg(0, SAM_UDP_RSTEP);
|
sam_putreg(0, SAM_UDP_RSTEP);
|
||||||
|
|
||||||
|
/* Copy any requests in the pending request queue to the working
|
||||||
|
* request queue.
|
||||||
|
*/
|
||||||
|
|
||||||
|
while ((req = sam_req_dequeue(&privep->pendq)) != NULL)
|
||||||
|
{
|
||||||
|
sam_req_enqueue(&privep->reqq, req);
|
||||||
|
}
|
||||||
|
|
||||||
/* Resuming any blocked data transfers on the endpoint */
|
/* Resuming any blocked data transfers on the endpoint */
|
||||||
|
|
||||||
if (epno == 0 || USB_ISEPIN(privep->ep.eplog))
|
if (epno == 0 || USB_ISEPIN(privep->ep.eplog))
|
||||||
@@ -3099,16 +3055,20 @@ static int sam_ep_submit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
|
|||||||
|
|
||||||
if (USB_ISEPIN(ep->eplog) || epno == EP0)
|
if (USB_ISEPIN(ep->eplog) || epno == EP0)
|
||||||
{
|
{
|
||||||
/* If the endpoint is stalled (or there is a stall pending), then fail
|
/* Check if the endpoint is stalled (or there is a stall pending) */
|
||||||
* any attempts to write through the endpoint.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (privep->stalled || privep->pending)
|
if (privep->stalled || privep->pending)
|
||||||
{
|
{
|
||||||
sam_req_abort(privep, privreq, -EBUSY);
|
/* Yes.. in this case, save the new they will get in a special
|
||||||
ulldbg("ERROR: stalled\n");
|
* "pending" they will get queue until the stall is cleared.
|
||||||
ret = -EPERM;
|
*/
|
||||||
|
|
||||||
|
ulldbg("Pending stall clear\n");
|
||||||
|
sam_req_enqueue(&privep->pendq, privreq);
|
||||||
|
usbtrace(TRACE_INREQQUEUED(epno), req->len);
|
||||||
|
ret = OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Add the new request to the request queue for the IN endpoint */
|
/* Add the new request to the request queue for the IN endpoint */
|
||||||
@@ -3213,10 +3173,10 @@ static int sam_ep_stallresume(struct usbdev_ep_s *ep, bool resume)
|
|||||||
{
|
{
|
||||||
/* Are there any unfinished write requests in the request queue? */
|
/* Are there any unfinished write requests in the request queue? */
|
||||||
|
|
||||||
if (!sam_rqempty(&privep->reqq)))
|
if (!sam_rqempty(&privep->reqq))
|
||||||
{
|
{
|
||||||
/* Just set a flag to indicate that the endpoint must be
|
/* Just set a flag to indicate that the endpoint must be
|
||||||
* stalled on the next TXCOMP interrupt when the requeust
|
* stalled on the next TXCOMP interrupt when the request
|
||||||
* queue becomes empty.
|
* queue becomes empty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1205,7 +1205,6 @@ static void sam_req_wrsetup(struct sam_usbdev_s *priv,
|
|||||||
uint8_t *fifo;
|
uint8_t *fifo;
|
||||||
uint8_t epno;
|
uint8_t epno;
|
||||||
int nbytes;
|
int nbytes;
|
||||||
int bytesleft;
|
|
||||||
|
|
||||||
/* Get the unadorned endpoint number */
|
/* Get the unadorned endpoint number */
|
||||||
|
|
||||||
@@ -1217,17 +1216,7 @@ static void sam_req_wrsetup(struct sam_usbdev_s *priv,
|
|||||||
|
|
||||||
/* Get the number of bytes remaining to be sent. */
|
/* Get the number of bytes remaining to be sent. */
|
||||||
|
|
||||||
bytesleft = privreq->req.len - privreq->req.xfrd;
|
nbytes = privreq->req.len - privreq->req.xfrd;
|
||||||
|
|
||||||
/* Clip the requested transfer size to the number of bytes actually
|
|
||||||
* available
|
|
||||||
*/
|
|
||||||
|
|
||||||
nbytes = bytesleft;
|
|
||||||
if (nbytes > bytesleft)
|
|
||||||
{
|
|
||||||
nbytes = bytesleft;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we are not sending a zero length packet, then clip the size to
|
/* If we are not sending a zero length packet, then clip the size to
|
||||||
* maxpacket and check if we need to send a following zero length packet.
|
* maxpacket and check if we need to send a following zero length packet.
|
||||||
|
|||||||
@@ -593,7 +593,7 @@ static inline int usbmsc_cmdinquiry(FAR struct usbmsc_dev_s *priv,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
memset(response, 0, SCSIRESP_INQUIRY_SIZEOF);
|
memset(response, 0, SCSIRESP_INQUIRY_SIZEOF);
|
||||||
priv->nreqbytes = SCSIRESP_INQUIRY_SIZEOF;
|
priv->nreqbytes = SCSIRESP_INQUIRY_SIZEOF;
|
||||||
|
|
||||||
#ifdef CONFIG_USBMSC_REMOVABLE
|
#ifdef CONFIG_USBMSC_REMOVABLE
|
||||||
response->flags1 = SCSIRESP_INQUIRYFLAGS1_RMB;
|
response->flags1 = SCSIRESP_INQUIRYFLAGS1_RMB;
|
||||||
@@ -1963,9 +1963,10 @@ static int usbmsc_cmdparsestate(FAR struct usbmsc_dev_s *priv)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDFINISH), priv->cdb[0]);
|
usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDFINISH), priv->cdb[0]);
|
||||||
priv->thstate = USBMSC_STATE_CMDFINISH;
|
priv->thstate = USBMSC_STATE_CMDFINISH;
|
||||||
ret = OK;
|
ret = OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2053,6 +2054,7 @@ static int usbmsc_cmdreadstate(FAR struct usbmsc_dev_s *priv)
|
|||||||
priv->nreqbytes = 0;
|
priv->nreqbytes = 0;
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
req = privreq->req;
|
req = privreq->req;
|
||||||
|
|
||||||
/* Transfer all of the data that will (1) fit into the request buffer, OR (2)
|
/* Transfer all of the data that will (1) fit into the request buffer, OR (2)
|
||||||
@@ -2305,7 +2307,7 @@ static int usbmsc_cmdfinishstate(FAR struct usbmsc_dev_s *priv)
|
|||||||
if (priv->cbwlen > 0)
|
if (priv->cbwlen > 0)
|
||||||
{
|
{
|
||||||
/* On most commands (the exception is outgoing, write commands),
|
/* On most commands (the exception is outgoing, write commands),
|
||||||
* the data has not not yet been sent.
|
* the data has not yet been sent.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (priv->nreqbytes > 0)
|
if (priv->nreqbytes > 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user