Squashed commit of the following:
drivers/usbdev/cdcacm.c: Change design for queuing RX packets that cannot be processed. Previous design had a logic problem that could cause data loss. drivers/usbdev/cdcacm: Fixes one of two know design issues. drivers/usbdev/cdcacm: First attempt to plug data leak in input flow control design. Still missing a few things.
This commit is contained in:
parent
d141242a25
commit
3fd0f67b62
@ -75,12 +75,19 @@
|
|||||||
|
|
||||||
/* Container to support a list of requests */
|
/* Container to support a list of requests */
|
||||||
|
|
||||||
struct cdcacm_req_s
|
struct cdcacm_wrreq_s
|
||||||
{
|
{
|
||||||
FAR struct cdcacm_req_s *flink; /* Implements a singly linked list */
|
FAR struct cdcacm_wrreq_s *flink; /* Implements a singly linked list */
|
||||||
FAR struct usbdev_req_s *req; /* The contained request */
|
FAR struct usbdev_req_s *req; /* The contained request */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct cdcacm_rdreq_s
|
||||||
|
{
|
||||||
|
FAR struct cdcacm_rdreq_s *flink; /* Implements a singly linked list */
|
||||||
|
FAR struct usbdev_req_s *req; /* The contained request */
|
||||||
|
uint16_t offset; /* Offset to valid data in the RX request */
|
||||||
|
};
|
||||||
|
|
||||||
/* This structure describes the internal state of the driver */
|
/* This structure describes the internal state of the driver */
|
||||||
|
|
||||||
struct cdcacm_dev_s
|
struct cdcacm_dev_s
|
||||||
@ -89,18 +96,17 @@ struct cdcacm_dev_s
|
|||||||
FAR struct usbdev_s *usbdev; /* usbdev driver pointer */
|
FAR struct usbdev_s *usbdev; /* usbdev driver pointer */
|
||||||
|
|
||||||
uint8_t config; /* Configuration number */
|
uint8_t config; /* Configuration number */
|
||||||
uint8_t nwrq; /* Number of queue write requests (in reqlist) */
|
uint8_t nwrq; /* Number of queue write requests (in txfree) */
|
||||||
uint8_t nrdq; /* Number of queue read requests (in epbulkout) */
|
uint8_t nrdq; /* Number of queue read requests (in epbulkout) */
|
||||||
uint8_t minor; /* The device minor number */
|
uint8_t minor; /* The device minor number */
|
||||||
|
uint8_t ctrlline; /* Buffered control line state */
|
||||||
#ifdef CONFIG_CDCACM_IFLOWCONTROL
|
#ifdef CONFIG_CDCACM_IFLOWCONTROL
|
||||||
uint8_t serialstate; /* State of the DSR/DCD */
|
uint8_t serialstate; /* State of the DSR/DCD */
|
||||||
bool iflow; /* True: input flow control is enabled */
|
bool iflow; /* True: input flow control is enabled */
|
||||||
bool upper; /* True: RX buffer is (nearly) full */
|
bool upper; /* True: RX buffer is (nearly) full */
|
||||||
#endif
|
#endif
|
||||||
bool rxenabled; /* true: UART RX "interrupts" enabled */
|
bool rxenabled; /* true: UART RX "interrupts" enabled */
|
||||||
int16_t rxhead; /* Working head; used when rx int disabled */
|
|
||||||
|
|
||||||
uint8_t ctrlline; /* Buffered control line state */
|
|
||||||
struct cdc_linecoding_s linecoding; /* Buffered line status */
|
struct cdc_linecoding_s linecoding; /* Buffered line status */
|
||||||
cdcacm_callback_t callback; /* Serial event callback function */
|
cdcacm_callback_t callback; /* Serial event callback function */
|
||||||
|
|
||||||
@ -108,17 +114,18 @@ struct cdcacm_dev_s
|
|||||||
FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint structure */
|
FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint structure */
|
||||||
FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint structure */
|
FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint structure */
|
||||||
FAR struct usbdev_req_s *ctrlreq; /* Allocated control request */
|
FAR struct usbdev_req_s *ctrlreq; /* Allocated control request */
|
||||||
struct sq_queue_s reqlist; /* List of write request containers */
|
struct sq_queue_s txfree; /* Available write request containers */
|
||||||
|
struct sq_queue_s rxpending; /* Pending read request containers */
|
||||||
|
|
||||||
struct usbdev_devinfo_s devinfo;
|
struct usbdev_devinfo_s devinfo;
|
||||||
|
|
||||||
/* Pre-allocated write request containers. The write requests will
|
/* Pre-allocated write request containers. The write requests will
|
||||||
* be linked in a free list (reqlist), and used to send requests to
|
* be linked in a free list (txfree), and used to send requests to
|
||||||
* EPBULKIN; Read requests will be queued in the EBULKOUT.
|
* EPBULKIN; Read requests will be queued in the EBULKOUT.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct cdcacm_req_s wrreqs[CONFIG_CDCACM_NWRREQS];
|
struct cdcacm_wrreq_s wrreqs[CONFIG_CDCACM_NWRREQS];
|
||||||
struct cdcacm_req_s rdreqs[CONFIG_CDCACM_NRDREQS];
|
struct cdcacm_rdreq_s rdreqs[CONFIG_CDCACM_NRDREQS];
|
||||||
|
|
||||||
/* Serial I/O buffers */
|
/* Serial I/O buffers */
|
||||||
|
|
||||||
@ -151,8 +158,11 @@ struct cdcacm_alloc_s
|
|||||||
static uint16_t cdcacm_fillrequest(FAR struct cdcacm_dev_s *priv,
|
static uint16_t cdcacm_fillrequest(FAR struct cdcacm_dev_s *priv,
|
||||||
uint8_t *reqbuf, uint16_t reqlen);
|
uint8_t *reqbuf, uint16_t reqlen);
|
||||||
static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv);
|
static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv);
|
||||||
static inline int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
|
static int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
|
||||||
uint8_t *reqbuf, uint16_t reqlen);
|
FAR struct cdcacm_rdreq_s *rdcontainer);
|
||||||
|
static int cdcacm_requeue_rdrequest(FAR struct cdcacm_dev_s *priv,
|
||||||
|
FAR struct cdcacm_rdreq_s *rdcontainer);
|
||||||
|
static int cdcacm_release_rxpending(FAR struct cdcacm_dev_s *priv);
|
||||||
|
|
||||||
/* Request helpers *********************************************************/
|
/* Request helpers *********************************************************/
|
||||||
|
|
||||||
@ -356,7 +366,7 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
|
|||||||
{
|
{
|
||||||
FAR struct usbdev_ep_s *ep;
|
FAR struct usbdev_ep_s *ep;
|
||||||
FAR struct usbdev_req_s *req;
|
FAR struct usbdev_req_s *req;
|
||||||
FAR struct cdcacm_req_s *reqcontainer;
|
FAR struct cdcacm_wrreq_s *wrcontainer;
|
||||||
uint16_t reqlen;
|
uint16_t reqlen;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
int len;
|
int len;
|
||||||
@ -383,18 +393,18 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
|
|||||||
|
|
||||||
uinfo("head=%d tail=%d nwrq=%d empty=%d\n",
|
uinfo("head=%d tail=%d nwrq=%d empty=%d\n",
|
||||||
priv->serdev.xmit.head, priv->serdev.xmit.tail,
|
priv->serdev.xmit.head, priv->serdev.xmit.tail,
|
||||||
priv->nwrq, sq_empty(&priv->reqlist));
|
priv->nwrq, sq_empty(&priv->txfree));
|
||||||
|
|
||||||
/* Get the maximum number of bytes that will fit into one bulk IN request */
|
/* Get the maximum number of bytes that will fit into one bulk IN request */
|
||||||
|
|
||||||
reqlen = MAX(CONFIG_CDCACM_BULKIN_REQLEN, ep->maxpacket);
|
reqlen = MAX(CONFIG_CDCACM_BULKIN_REQLEN, ep->maxpacket);
|
||||||
|
|
||||||
while (!sq_empty(&priv->reqlist))
|
while (!sq_empty(&priv->txfree))
|
||||||
{
|
{
|
||||||
/* Peek at the request in the container at the head of the list */
|
/* Peek at the request in the container at the head of the list */
|
||||||
|
|
||||||
reqcontainer = (FAR struct cdcacm_req_s *)sq_peek(&priv->reqlist);
|
wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_peek(&priv->txfree);
|
||||||
req = reqcontainer->req;
|
req = wrcontainer->req;
|
||||||
|
|
||||||
/* Fill the request with serial TX data */
|
/* Fill the request with serial TX data */
|
||||||
|
|
||||||
@ -403,13 +413,13 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
|
|||||||
{
|
{
|
||||||
/* Remove the empty container from the request list */
|
/* Remove the empty container from the request list */
|
||||||
|
|
||||||
(void)sq_remfirst(&priv->reqlist);
|
(void)sq_remfirst(&priv->txfree);
|
||||||
priv->nwrq--;
|
priv->nwrq--;
|
||||||
|
|
||||||
/* Then submit the request to the endpoint */
|
/* Then submit the request to the endpoint */
|
||||||
|
|
||||||
req->len = len;
|
req->len = len;
|
||||||
req->priv = reqcontainer;
|
req->priv = wrcontainer;
|
||||||
req->flags = USBDEV_REQFLAGS_NULLPKT;
|
req->flags = USBDEV_REQFLAGS_NULLPKT;
|
||||||
ret = EP_SUBMIT(ep, req);
|
ret = EP_SUBMIT(ep, req);
|
||||||
if (ret != OK)
|
if (ret != OK)
|
||||||
@ -441,11 +451,14 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static inline int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
|
static int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
|
||||||
FAR uint8_t *reqbuf, uint16_t reqlen)
|
FAR struct cdcacm_rdreq_s *rdcontainer)
|
||||||
{
|
{
|
||||||
FAR uart_dev_t *serdev = &priv->serdev;
|
FAR uart_dev_t *serdev;
|
||||||
FAR struct uart_buffer_s *recv = &serdev->recv;
|
FAR struct uart_buffer_s *recv;
|
||||||
|
FAR struct usbdev_req_s *req;
|
||||||
|
FAR uint8_t *reqbuf;
|
||||||
|
uint16_t reqlen;
|
||||||
uint16_t currhead;
|
uint16_t currhead;
|
||||||
uint16_t nexthead;
|
uint16_t nexthead;
|
||||||
uint16_t nbytes = 0;
|
uint16_t nbytes = 0;
|
||||||
@ -453,22 +466,25 @@ static inline int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
|
|||||||
uinfo("head=%d tail=%d nrdq=%d reqlen=%d\n",
|
uinfo("head=%d tail=%d nrdq=%d reqlen=%d\n",
|
||||||
priv->serdev.recv.head, priv->serdev.recv.tail, priv->nrdq, reqlen);
|
priv->serdev.recv.head, priv->serdev.recv.tail, priv->nrdq, reqlen);
|
||||||
|
|
||||||
/* Get the next head index. During the time that RX interrupts are
|
DEBUGASSERT(priv != NULL && rdcontainer != NULL);
|
||||||
* disabled, the the serial driver will be extracting data from the
|
#ifdef CONFIG_CDCACM_IFLOWCONTROL
|
||||||
* circular buffer and modifying recv.tail. During this time, we should
|
DEBUGASSERT(priv->rxenabled && !priv->iflow);
|
||||||
* avoid modifying recv.head; Instead we will use a shadow copy of the
|
#else
|
||||||
* index. When interrupts are restored, the real recv.head will be
|
DEBUGASSERT(priv->rxenabled);
|
||||||
* updated with this index.
|
#endif
|
||||||
*/
|
|
||||||
|
req = rdcontainer->req;
|
||||||
|
DEBUGASSERT(req != NULL);
|
||||||
|
|
||||||
|
reqbuf = &req->buf[rdcontainer->offset];
|
||||||
|
reqlen = req->xfrd - rdcontainer->offset;
|
||||||
|
|
||||||
|
serdev = &priv->serdev;
|
||||||
|
recv = &serdev->recv;
|
||||||
|
|
||||||
|
/* Get the next head index. */
|
||||||
|
|
||||||
if (priv->rxenabled)
|
|
||||||
{
|
|
||||||
currhead = recv->head;
|
currhead = recv->head;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currhead = priv->rxhead;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pre-calculate the head index and check for wrap around. We need to do
|
/* Pre-calculate the head index and check for wrap around. We need to do
|
||||||
* this so that we can determine if the circular buffer will overrun
|
* this so that we can determine if the circular buffer will overrun
|
||||||
@ -514,18 +530,9 @@ static inline int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write back the head pointer using the shadow index if RX "interrupts"
|
/* Write back the head pointer. */
|
||||||
* are disabled.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (priv->rxenabled)
|
|
||||||
{
|
|
||||||
recv->head = currhead;
|
recv->head = currhead;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
priv->rxhead = currhead;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If data was added to the incoming serial buffer, then wake up any
|
/* If data was added to the incoming serial buffer, then wake up any
|
||||||
* threads is waiting for incoming data. If we are running in an interrupt
|
* threads is waiting for incoming data. If we are running in an interrupt
|
||||||
@ -533,33 +540,128 @@ static inline int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
|
|||||||
* returns.
|
* returns.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (priv->rxenabled && nbytes > 0)
|
if (nbytes > 0)
|
||||||
{
|
{
|
||||||
uart_datareceived(serdev);
|
uart_datareceived(serdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return an overrun error if the entire packet could not be transferred.
|
/* Return an overrun error if the entire packet could not be transferred. */
|
||||||
*
|
|
||||||
* REVISIT: In the case where RX flow control is enabled, there should
|
|
||||||
* be no data loss. I could imagine some race conditions where dropping
|
|
||||||
* buffer like this could cause data loss even with RX flow control
|
|
||||||
* enabled.
|
|
||||||
*
|
|
||||||
* Perhaps packets should not be dropped if RX flow control is active;
|
|
||||||
* pehaps the packet should be buffered and used later when there is again
|
|
||||||
* space in the RX data buffer. This could, of course, result in NAKing
|
|
||||||
* which is something that I want to avoid.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (nbytes < reqlen)
|
if (nbytes < reqlen)
|
||||||
{
|
{
|
||||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RXOVERRUN), 0);
|
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RXOVERRUN), 0);
|
||||||
|
rdcontainer->offset = nbytes;
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: cdcacm_requeue_rdrequest
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Add any pending RX packets to the upper half serial drivers RX buffer.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static int cdcacm_requeue_rdrequest(FAR struct cdcacm_dev_s *priv,
|
||||||
|
FAR struct cdcacm_rdreq_s *rdcontainer)
|
||||||
|
{
|
||||||
|
FAR struct usbdev_req_s *req;
|
||||||
|
FAR struct usbdev_ep_s *ep;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
DEBUGASSERT(priv != NULL && rdcontainer != NULL);
|
||||||
|
rdcontainer->offset = 0;
|
||||||
|
|
||||||
|
req = rdcontainer->req;
|
||||||
|
DEBUGASSERT(req != NULL);
|
||||||
|
|
||||||
|
/* Requeue the read request */
|
||||||
|
|
||||||
|
ep = priv->epbulkout;
|
||||||
|
req->len = ep->maxpacket;
|
||||||
|
ret = EP_SUBMIT(ep, req);
|
||||||
|
if (ret != OK)
|
||||||
|
{
|
||||||
|
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT),
|
||||||
|
(uint16_t)-req->result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: cdcacm_release_rxpending
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Add any pending RX packets to the upper half serial drivers RX buffer.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static int cdcacm_release_rxpending(FAR struct cdcacm_dev_s *priv)
|
||||||
|
{
|
||||||
|
FAR struct cdcacm_rdreq_s *rdcontainer;
|
||||||
|
irqstate_t flags;
|
||||||
|
int ret = -EBUSY;
|
||||||
|
|
||||||
|
/* If RX "interrupts" are enable and if input flow control is not in effect,
|
||||||
|
* then pass the packet at the head of the pending RX packet list to the to
|
||||||
|
* the upper serial layer. Otherwise, let the packet continue to pend the
|
||||||
|
* priv->rxpending list until the upper serial layer is able to buffer it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef CONFIG_CDCACM_IFLOWCONTROL
|
||||||
|
if (priv->rxenabled && !priv->iflow)
|
||||||
|
#else
|
||||||
|
if (priv->rxenabled)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/* Process pending RX packets while the queue is not empty and while
|
||||||
|
* no errors occur. NOTE that the priv->rxpending queue is accessed
|
||||||
|
* from interrupt level processing and, hence, interrupts must be
|
||||||
|
* disabled throughout the following.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ret = OK;
|
||||||
|
flags = enter_critical_section();
|
||||||
|
|
||||||
|
while (!sq_empty(&priv->rxpending))
|
||||||
|
{
|
||||||
|
/* Process each packet in the priv->rxpending list */
|
||||||
|
|
||||||
|
rdcontainer = (FAR struct cdcacm_rdreq_s *)
|
||||||
|
sq_peek(&priv->rxpending);
|
||||||
|
DEBUGASSERT(rdcontainer != NULL);
|
||||||
|
|
||||||
|
/* cdcacm_recvpacket() will return OK if the entire packet was
|
||||||
|
* successful buffered. In the case of RX buffer overrun,
|
||||||
|
* cdcacm_recvpacket() will return a failure (-ENOSPC) and will
|
||||||
|
* set the req->offset field
|
||||||
|
*/
|
||||||
|
|
||||||
|
ret = cdcacm_recvpacket(priv, rdcontainer);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
uwarn("WARNING: RX buffer full\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The entire packet was processed and may be removed from the
|
||||||
|
* pending RX list and returned to the DCD.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(void)sq_remfirst(&priv->rxpending);
|
||||||
|
ret = cdcacm_requeue_rdrequest(priv, rdcontainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
leave_critical_section(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: cdcacm_allocreq
|
* Name: cdcacm_allocreq
|
||||||
*
|
*
|
||||||
@ -635,7 +737,7 @@ static int cdcacm_serialstate(FAR struct cdcacm_dev_s *priv)
|
|||||||
{
|
{
|
||||||
FAR struct usbdev_ep_s *ep;
|
FAR struct usbdev_ep_s *ep;
|
||||||
FAR struct usbdev_req_s *req;
|
FAR struct usbdev_req_s *req;
|
||||||
FAR struct cdcacm_req_s *reqcontainer;
|
FAR struct cdcacm_wrreq_s *wrcontainer;
|
||||||
FAR struct cdc_notification_s *notify;
|
FAR struct cdc_notification_s *notify;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
int ret;
|
int ret;
|
||||||
@ -659,8 +761,8 @@ static int cdcacm_serialstate(FAR struct cdcacm_dev_s *priv)
|
|||||||
|
|
||||||
/* Remove the next container from the request list */
|
/* Remove the next container from the request list */
|
||||||
|
|
||||||
reqcontainer = (FAR struct cdcacm_req_s *)sq_remfirst(&priv->reqlist);
|
wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree);
|
||||||
if (reqcontainer == NULL)
|
if (wrcontainer == NULL)
|
||||||
{
|
{
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto errout_with_flags;
|
goto errout_with_flags;
|
||||||
@ -672,8 +774,8 @@ static int cdcacm_serialstate(FAR struct cdcacm_dev_s *priv)
|
|||||||
|
|
||||||
/* Format the SerialState notifcation */
|
/* Format the SerialState notifcation */
|
||||||
|
|
||||||
DEBUGASSERT(reqcontainer->req != NULL);
|
DEBUGASSERT(wrcontainer->req != NULL);
|
||||||
req = reqcontainer->req;
|
req = wrcontainer->req;
|
||||||
|
|
||||||
DEBUGASSERT(req->buf != NULL);
|
DEBUGASSERT(req->buf != NULL);
|
||||||
notify = (FAR struct cdc_notification_s *)req->buf;
|
notify = (FAR struct cdc_notification_s *)req->buf;
|
||||||
@ -693,7 +795,7 @@ static int cdcacm_serialstate(FAR struct cdcacm_dev_s *priv)
|
|||||||
/* Then submit the request to the endpoint */
|
/* Then submit the request to the endpoint */
|
||||||
|
|
||||||
req->len = SIZEOF_NOTIFICATION_S(2);
|
req->len = SIZEOF_NOTIFICATION_S(2);
|
||||||
req->priv = reqcontainer;
|
req->priv = wrcontainer;
|
||||||
req->flags = USBDEV_REQFLAGS_NULLPKT;
|
req->flags = USBDEV_REQFLAGS_NULLPKT;
|
||||||
ret = EP_SUBMIT(ep, req);
|
ret = EP_SUBMIT(ep, req);
|
||||||
|
|
||||||
@ -950,9 +1052,9 @@ static void cdcacm_ep0incomplete(FAR struct usbdev_ep_s *ep,
|
|||||||
static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
|
static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
|
||||||
FAR struct usbdev_req_s *req)
|
FAR struct usbdev_req_s *req)
|
||||||
{
|
{
|
||||||
|
FAR struct cdcacm_rdreq_s *rdcontainer;
|
||||||
FAR struct cdcacm_dev_s *priv;
|
FAR struct cdcacm_dev_s *priv;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
|
|
||||||
@ -968,36 +1070,47 @@ static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
|
|||||||
|
|
||||||
priv = (FAR struct cdcacm_dev_s *)ep->priv;
|
priv = (FAR struct cdcacm_dev_s *)ep->priv;
|
||||||
|
|
||||||
|
/* Get the container of the read request */
|
||||||
|
|
||||||
|
rdcontainer = (FAR struct cdcacm_rdreq_s *)req->priv;
|
||||||
|
DEBUGASSERT(rdcontainer != NULL);
|
||||||
|
|
||||||
/* Process the received data unless this is some unusual condition */
|
/* Process the received data unless this is some unusual condition */
|
||||||
|
|
||||||
flags = enter_critical_section();
|
flags = enter_critical_section();
|
||||||
switch (req->result)
|
switch (req->result)
|
||||||
{
|
{
|
||||||
case 0: /* Normal completion */
|
case 0: /* Normal completion */
|
||||||
|
{
|
||||||
usbtrace(TRACE_CLASSRDCOMPLETE, priv->nrdq);
|
usbtrace(TRACE_CLASSRDCOMPLETE, priv->nrdq);
|
||||||
cdcacm_recvpacket(priv, req->buf, req->xfrd);
|
|
||||||
|
/* Place the incoming packet at the end of pending RX packet list. */
|
||||||
|
|
||||||
|
sq_addlast((FAR sq_entry_t *)rdcontainer, &priv->rxpending);
|
||||||
|
rdcontainer->offset = 0;
|
||||||
|
|
||||||
|
/* Then process all pending RX packet starting at the head of the
|
||||||
|
* list
|
||||||
|
*/
|
||||||
|
|
||||||
|
(void)cdcacm_release_rxpending(priv);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case -ESHUTDOWN: /* Disconnection */
|
case -ESHUTDOWN: /* Disconnection */
|
||||||
|
{
|
||||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0);
|
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0);
|
||||||
priv->nrdq--;
|
priv->nrdq--;
|
||||||
leave_critical_section(flags);
|
}
|
||||||
return;
|
break;
|
||||||
|
|
||||||
default: /* Some other error occurred */
|
default: /* Some other error occurred */
|
||||||
|
{
|
||||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDUNEXPECTED),
|
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDUNEXPECTED),
|
||||||
(uint16_t)-req->result);
|
(uint16_t)-req->result);
|
||||||
|
cdcacm_requeue_rdrequest(priv, rdcontainer);
|
||||||
break;
|
break;
|
||||||
};
|
}
|
||||||
|
|
||||||
/* Requeue the read request */
|
|
||||||
|
|
||||||
req->len = ep->maxpacket;
|
|
||||||
ret = EP_SUBMIT(ep, req);
|
|
||||||
if (ret != OK)
|
|
||||||
{
|
|
||||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT),
|
|
||||||
(uint16_t)-req->result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
leave_critical_section(flags);
|
leave_critical_section(flags);
|
||||||
@ -1016,7 +1129,7 @@ static void cdcacm_wrcomplete(FAR struct usbdev_ep_s *ep,
|
|||||||
FAR struct usbdev_req_s *req)
|
FAR struct usbdev_req_s *req)
|
||||||
{
|
{
|
||||||
FAR struct cdcacm_dev_s *priv;
|
FAR struct cdcacm_dev_s *priv;
|
||||||
FAR struct cdcacm_req_s *reqcontainer;
|
FAR struct cdcacm_wrreq_s *wrcontainer;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
@ -1032,12 +1145,12 @@ static void cdcacm_wrcomplete(FAR struct usbdev_ep_s *ep,
|
|||||||
/* Extract references to our private data */
|
/* Extract references to our private data */
|
||||||
|
|
||||||
priv = (FAR struct cdcacm_dev_s *)ep->priv;
|
priv = (FAR struct cdcacm_dev_s *)ep->priv;
|
||||||
reqcontainer = (FAR struct cdcacm_req_s *)req->priv;
|
wrcontainer = (FAR struct cdcacm_wrreq_s *)req->priv;
|
||||||
|
|
||||||
/* Return the write request to the free list */
|
/* Return the write request to the free list */
|
||||||
|
|
||||||
flags = enter_critical_section();
|
flags = enter_critical_section();
|
||||||
sq_addlast((FAR sq_entry_t *)reqcontainer, &priv->reqlist);
|
sq_addlast((FAR sq_entry_t *)wrcontainer, &priv->txfree);
|
||||||
priv->nwrq++;
|
priv->nwrq++;
|
||||||
leave_critical_section(flags);
|
leave_critical_section(flags);
|
||||||
|
|
||||||
@ -1085,7 +1198,8 @@ static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver,
|
|||||||
FAR struct usbdev_s *dev)
|
FAR struct usbdev_s *dev)
|
||||||
{
|
{
|
||||||
FAR struct cdcacm_dev_s *priv = ((FAR struct cdcacm_driver_s *)driver)->dev;
|
FAR struct cdcacm_dev_s *priv = ((FAR struct cdcacm_driver_s *)driver)->dev;
|
||||||
FAR struct cdcacm_req_s *reqcontainer;
|
FAR struct cdcacm_wrreq_s *wrcontainer;
|
||||||
|
FAR struct cdcacm_rdreq_s *rdcontainer;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
uint16_t reqlen;
|
uint16_t reqlen;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1175,17 +1289,18 @@ static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver,
|
|||||||
|
|
||||||
for (i = 0; i < CONFIG_CDCACM_NRDREQS; i++)
|
for (i = 0; i < CONFIG_CDCACM_NRDREQS; i++)
|
||||||
{
|
{
|
||||||
reqcontainer = &priv->rdreqs[i];
|
rdcontainer = &priv->rdreqs[i];
|
||||||
reqcontainer->req = cdcacm_allocreq(priv->epbulkout, reqlen);
|
rdcontainer->req = cdcacm_allocreq(priv->epbulkout, reqlen);
|
||||||
if (reqcontainer->req == NULL)
|
if (rdcontainer->req == NULL)
|
||||||
{
|
{
|
||||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDALLOCREQ), -ENOMEM);
|
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDALLOCREQ), -ENOMEM);
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto errout;
|
goto errout;
|
||||||
}
|
}
|
||||||
|
|
||||||
reqcontainer->req->priv = reqcontainer;
|
rdcontainer->offset = 0;
|
||||||
reqcontainer->req->callback = cdcacm_rdcomplete;
|
rdcontainer->req->priv = rdcontainer;
|
||||||
|
rdcontainer->req->callback = cdcacm_rdcomplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pre-allocate write request containers and put in a free list. The
|
/* Pre-allocate write request containers and put in a free list. The
|
||||||
@ -1211,20 +1326,20 @@ static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver,
|
|||||||
|
|
||||||
for (i = 0; i < CONFIG_CDCACM_NWRREQS; i++)
|
for (i = 0; i < CONFIG_CDCACM_NWRREQS; i++)
|
||||||
{
|
{
|
||||||
reqcontainer = &priv->wrreqs[i];
|
wrcontainer = &priv->wrreqs[i];
|
||||||
reqcontainer->req = cdcacm_allocreq(priv->epbulkin, reqlen);
|
wrcontainer->req = cdcacm_allocreq(priv->epbulkin, reqlen);
|
||||||
if (reqcontainer->req == NULL)
|
if (wrcontainer->req == NULL)
|
||||||
{
|
{
|
||||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRALLOCREQ), -ENOMEM);
|
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRALLOCREQ), -ENOMEM);
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto errout;
|
goto errout;
|
||||||
}
|
}
|
||||||
|
|
||||||
reqcontainer->req->priv = reqcontainer;
|
wrcontainer->req->priv = wrcontainer;
|
||||||
reqcontainer->req->callback = cdcacm_wrcomplete;
|
wrcontainer->req->callback = cdcacm_wrcomplete;
|
||||||
|
|
||||||
flags = enter_critical_section();
|
flags = enter_critical_section();
|
||||||
sq_addlast((FAR sq_entry_t *)reqcontainer, &priv->reqlist);
|
sq_addlast((FAR sq_entry_t *)wrcontainer, &priv->txfree);
|
||||||
priv->nwrq++; /* Count of write requests available */
|
priv->nwrq++; /* Count of write requests available */
|
||||||
leave_critical_section(flags);
|
leave_critical_section(flags);
|
||||||
}
|
}
|
||||||
@ -1261,7 +1376,8 @@ static void cdcacm_unbind(FAR struct usbdevclass_driver_s *driver,
|
|||||||
FAR struct usbdev_s *dev)
|
FAR struct usbdev_s *dev)
|
||||||
{
|
{
|
||||||
FAR struct cdcacm_dev_s *priv;
|
FAR struct cdcacm_dev_s *priv;
|
||||||
FAR struct cdcacm_req_s *reqcontainer;
|
FAR struct cdcacm_wrreq_s *wrcontainer;
|
||||||
|
FAR struct cdcacm_rdreq_s *rdcontainer;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -1324,11 +1440,11 @@ static void cdcacm_unbind(FAR struct usbdevclass_driver_s *driver,
|
|||||||
DEBUGASSERT(priv->nrdq == 0);
|
DEBUGASSERT(priv->nrdq == 0);
|
||||||
for (i = 0; i < CONFIG_CDCACM_NRDREQS; i++)
|
for (i = 0; i < CONFIG_CDCACM_NRDREQS; i++)
|
||||||
{
|
{
|
||||||
reqcontainer = &priv->rdreqs[i];
|
rdcontainer = &priv->rdreqs[i];
|
||||||
if (reqcontainer->req)
|
if (rdcontainer->req)
|
||||||
{
|
{
|
||||||
cdcacm_freereq(priv->epbulkout, reqcontainer->req);
|
cdcacm_freereq(priv->epbulkout, rdcontainer->req);
|
||||||
reqcontainer->req = NULL;
|
rdcontainer->req = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1347,12 +1463,12 @@ static void cdcacm_unbind(FAR struct usbdevclass_driver_s *driver,
|
|||||||
flags = enter_critical_section();
|
flags = enter_critical_section();
|
||||||
DEBUGASSERT(priv->nwrq == CONFIG_CDCACM_NWRREQS);
|
DEBUGASSERT(priv->nwrq == CONFIG_CDCACM_NWRREQS);
|
||||||
|
|
||||||
while (!sq_empty(&priv->reqlist))
|
while (!sq_empty(&priv->txfree))
|
||||||
{
|
{
|
||||||
reqcontainer = (struct cdcacm_req_s *)sq_remfirst(&priv->reqlist);
|
wrcontainer = (struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree);
|
||||||
if (reqcontainer->req != NULL)
|
if (wrcontainer->req != NULL)
|
||||||
{
|
{
|
||||||
cdcacm_freereq(priv->epbulkin, reqcontainer->req);
|
cdcacm_freereq(priv->epbulkin, wrcontainer->req);
|
||||||
priv->nwrq--; /* Number of write requests queued */
|
priv->nwrq--; /* Number of write requests queued */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1822,7 +1938,6 @@ static void cdcacm_disconnect(FAR struct usbdevclass_driver_s *driver,
|
|||||||
|
|
||||||
priv->serdev.xmit.head = 0;
|
priv->serdev.xmit.head = 0;
|
||||||
priv->serdev.xmit.tail = 0;
|
priv->serdev.xmit.tail = 0;
|
||||||
priv->rxhead = 0;
|
|
||||||
leave_critical_section(flags);
|
leave_critical_section(flags);
|
||||||
|
|
||||||
/* Perform the soft connect function so that we will we can be
|
/* Perform the soft connect function so that we will we can be
|
||||||
@ -2156,16 +2271,12 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|||||||
iflow = ((termiosp->c_cflag & CRTS_IFLOW) != 0);
|
iflow = ((termiosp->c_cflag & CRTS_IFLOW) != 0);
|
||||||
if (iflow != priv->iflow)
|
if (iflow != priv->iflow)
|
||||||
{
|
{
|
||||||
/* The input flow control state has changed. Save the new
|
|
||||||
* flow control setting.
|
|
||||||
*/
|
|
||||||
|
|
||||||
priv->iflow = iflow;
|
|
||||||
|
|
||||||
/* Check if flow control has been disabled. */
|
/* Check if flow control has been disabled. */
|
||||||
|
|
||||||
if (!iflow)
|
if (!iflow)
|
||||||
{
|
{
|
||||||
|
irqstate_t flags;
|
||||||
|
|
||||||
/* Flow control has been disabled. We need to make sure
|
/* Flow control has been disabled. We need to make sure
|
||||||
* that DSR is set unconditionally.
|
* that DSR is set unconditionally.
|
||||||
*/
|
*/
|
||||||
@ -2175,6 +2286,19 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|||||||
priv->serialstate |= (CDCACM_UART_DSR | CDCACM_UART_DCD);
|
priv->serialstate |= (CDCACM_UART_DSR | CDCACM_UART_DCD);
|
||||||
ret = cdcacm_serialstate(priv);
|
ret = cdcacm_serialstate(priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* During the time that flow control was disabled, incoming
|
||||||
|
* packets were queued in priv->rxpending. We must now
|
||||||
|
* process all of them (unless RX interrupts are also
|
||||||
|
* disabled)
|
||||||
|
*/
|
||||||
|
|
||||||
|
(void)cdcacm_release_rxpending(priv);
|
||||||
|
|
||||||
|
/* Save the new flow control setting. */
|
||||||
|
|
||||||
|
priv->iflow = false;
|
||||||
|
leave_critical_section(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flow control has been enabled. If the RX buffer is already
|
/* Flow control has been enabled. If the RX buffer is already
|
||||||
@ -2187,12 +2311,17 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|||||||
|
|
||||||
else if (priv->upper)
|
else if (priv->upper)
|
||||||
{
|
{
|
||||||
/* In this transition, DSR should already be set */
|
/* Save the new flow control setting. */
|
||||||
|
|
||||||
|
priv->iflow = true;
|
||||||
priv->serialstate &= ~CDCACM_UART_DSR;
|
priv->serialstate &= ~CDCACM_UART_DSR;
|
||||||
priv->serialstate |= CDCACM_UART_DCD;
|
priv->serialstate |= CDCACM_UART_DCD;
|
||||||
ret = cdcacm_serialstate(priv);
|
ret = cdcacm_serialstate(priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* RX "interrupts are no longer disabled */
|
||||||
|
|
||||||
|
priv->rxenabled = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -2299,12 +2428,17 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|||||||
* 3. With enable==false when the port is closed (just before cdcuart_detach
|
* 3. With enable==false when the port is closed (just before cdcuart_detach
|
||||||
* and cdcuart_shutdown are called).
|
* and cdcuart_shutdown are called).
|
||||||
*
|
*
|
||||||
|
* Assumptions:
|
||||||
|
* Called from the serial upper-half driver running on the thread of
|
||||||
|
* execution of the caller of the driver or, possibly, on from the
|
||||||
|
* USB interrupt handler (at least for the case where the RX interrupt
|
||||||
|
* is disabled)
|
||||||
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
|
static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
|
||||||
{
|
{
|
||||||
FAR struct cdcacm_dev_s *priv;
|
FAR struct cdcacm_dev_s *priv;
|
||||||
FAR uart_dev_t *serdev;
|
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
|
|
||||||
usbtrace(CDCACM_CLASSAPI_RXINT, (uint16_t)enable);
|
usbtrace(CDCACM_CLASSAPI_RXINT, (uint16_t)enable);
|
||||||
@ -2322,7 +2456,6 @@ static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
|
|||||||
/* Extract reference to private data */
|
/* Extract reference to private data */
|
||||||
|
|
||||||
priv = (FAR struct cdcacm_dev_s *)dev->priv;
|
priv = (FAR struct cdcacm_dev_s *)dev->priv;
|
||||||
serdev = &priv->serdev;
|
|
||||||
|
|
||||||
/* We need exclusive access to the RX buffer and private structure
|
/* We need exclusive access to the RX buffer and private structure
|
||||||
* in the following.
|
* in the following.
|
||||||
@ -2333,26 +2466,20 @@ static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
|
|||||||
{
|
{
|
||||||
/* RX "interrupts" are enabled. Is this a transition from disabled
|
/* RX "interrupts" are enabled. Is this a transition from disabled
|
||||||
* to enabled state?
|
* to enabled state?
|
||||||
|
*
|
||||||
|
* We also need to check if input control flow is in effect. If so,
|
||||||
|
* then we should not call uart_datareceived() until both
|
||||||
|
* priv->rxenabled is true and priv->iflow are false.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!priv->rxenabled)
|
if (!priv->rxenabled)
|
||||||
{
|
{
|
||||||
/* Yes. During the time that RX interrupts are disabled, the
|
/* Yes. During the time that RX interrupts are disabled,
|
||||||
* the serial driver will be extracting data from the circular
|
* incoming packets were queued in priv->rxpending. We must
|
||||||
* buffer and modifying recv.tail. During this time, we
|
* now free all of them (unless flow control becomes enabled)
|
||||||
* should avoid modifying recv.head; When interrupts are restored,
|
|
||||||
* we can update the head pointer for all of the data that we
|
|
||||||
* put into circular buffer while "interrupts" were disabled.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (priv->rxhead != serdev->recv.head)
|
(void)cdcacm_release_rxpending(priv);
|
||||||
{
|
|
||||||
serdev->recv.head = priv->rxhead;
|
|
||||||
|
|
||||||
/* Yes... signal the availability of new data */
|
|
||||||
|
|
||||||
uart_datareceived(serdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RX "interrupts are no longer disabled */
|
/* RX "interrupts are no longer disabled */
|
||||||
|
|
||||||
@ -2364,17 +2491,8 @@ static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
|
|||||||
* to disabled state?
|
* to disabled state?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
else if (priv->rxenabled)
|
else
|
||||||
{
|
{
|
||||||
/* Yes. During the time that RX interrupts are disabled, the
|
|
||||||
* the serial driver will be extracting data from the circular
|
|
||||||
* buffer and modifying recv.tail. During this time, we
|
|
||||||
* should avoid modifying recv.head; When interrupts are disabled,
|
|
||||||
* we use a shadow index and continue adding data to the circular
|
|
||||||
* buffer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
priv->rxhead = serdev->recv.head;
|
|
||||||
priv->rxenabled = false;
|
priv->rxenabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2557,7 +2675,7 @@ static bool cdcuart_txempty(FAR struct uart_dev_s *dev)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* When all of the allocated write requests have been returned to the
|
/* When all of the allocated write requests have been returned to the
|
||||||
* reqlist, then there is no longer any TX data in flight.
|
* txfree, then there is no longer any TX data in flight.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return priv->nwrq >= CONFIG_CDCACM_NWRREQS;
|
return priv->nwrq >= CONFIG_CDCACM_NWRREQS;
|
||||||
@ -2616,7 +2734,8 @@ int cdcacm_classobject(int minor, FAR struct usbdev_devinfo_s *devinfo,
|
|||||||
/* Initialize the USB serial driver structure */
|
/* Initialize the USB serial driver structure */
|
||||||
|
|
||||||
memset(priv, 0, sizeof(struct cdcacm_dev_s));
|
memset(priv, 0, sizeof(struct cdcacm_dev_s));
|
||||||
sq_init(&priv->reqlist);
|
sq_init(&priv->txfree);
|
||||||
|
sq_init(&priv->rxpending);
|
||||||
|
|
||||||
priv->minor = minor;
|
priv->minor = minor;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user