mirror of
https://github.com/apache/nuttx.git
synced 2026-08-11 07:25:13 +00:00
cdcacm: use serial public APIs
Signed-off-by: yangsong8 <yangsong8@xiaomi.com>
This commit is contained in:
parent
b319c27f03
commit
e3fcabaec2
1 changed files with 114 additions and 279 deletions
|
|
@ -103,6 +103,7 @@ struct cdcacm_dev_s
|
|||
bool upper; /* True: RX buffer is (nearly) full */
|
||||
#endif
|
||||
bool rxenabled; /* true: UART RX "interrupts" enabled */
|
||||
bool issending;
|
||||
|
||||
struct cdc_linecoding_s linecoding; /* Buffered line status */
|
||||
cdcacm_callback_t callback; /* Serial event callback function */
|
||||
|
|
@ -153,11 +154,7 @@ struct cdcacm_alloc_s
|
|||
|
||||
/* Transfer helpers *********************************************************/
|
||||
|
||||
static uint16_t cdcacm_fillrequest(FAR struct cdcacm_dev_s *priv,
|
||||
uint8_t *reqbuf, uint16_t reqlen);
|
||||
static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv);
|
||||
static int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
|
||||
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);
|
||||
|
|
@ -223,6 +220,12 @@ static bool cdcuart_rxflowcontrol(FAR struct uart_dev_s *dev,
|
|||
static void cdcuart_txint(FAR struct uart_dev_s *dev, bool enable);
|
||||
static bool cdcuart_txempty(FAR struct uart_dev_s *dev);
|
||||
static int cdcuart_release(FAR struct uart_dev_s *dev);
|
||||
static bool cdcuart_rxavailable(FAR struct uart_dev_s *dev);
|
||||
static ssize_t cdcuart_recvbuf(FAR struct uart_dev_s *dev,
|
||||
FAR void *buf, size_t len);
|
||||
static bool cdcuart_txready(FAR struct uart_dev_s *dev);
|
||||
static ssize_t cdcuart_sendbuf(FAR struct uart_dev_s *dev,
|
||||
FAR const void *buf, size_t len);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
|
|
@ -256,7 +259,7 @@ static const struct uart_ops_s g_uartops =
|
|||
cdcuart_ioctl, /* ioctl */
|
||||
NULL, /* receive */
|
||||
cdcuart_rxint, /* rxinit */
|
||||
NULL, /* rxavailable */
|
||||
cdcuart_rxavailable, /* rxavailable */
|
||||
#ifdef CONFIG_SERIAL_IFLOWCONTROL
|
||||
cdcuart_rxflowcontrol, /* rxflowcontrol */
|
||||
#endif
|
||||
|
|
@ -272,9 +275,11 @@ static const struct uart_ops_s g_uartops =
|
|||
#endif
|
||||
NULL, /* send */
|
||||
cdcuart_txint, /* txinit */
|
||||
NULL, /* txready */
|
||||
cdcuart_txready, /* txready */
|
||||
cdcuart_txempty, /* txempty */
|
||||
cdcuart_release /* release */
|
||||
cdcuart_release, /* release */
|
||||
cdcuart_recvbuf, /* recvbuf */
|
||||
cdcuart_sendbuf /* sendbuf */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -282,70 +287,68 @@ static const struct uart_ops_s g_uartops =
|
|||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: cdcacm_fillrequest
|
||||
* Name: cdcuart_txready
|
||||
*
|
||||
* Description:
|
||||
* If there is data to send it is copied to the given buffer. Called
|
||||
* either to initiate the first write operation, or from the completion
|
||||
* interrupt handler service consecutive write operations.
|
||||
*
|
||||
* NOTE: The USB serial driver does not use the serial drivers
|
||||
* uart_xmitchars() API. That logic is essentially duplicated here because
|
||||
* unlike UART hardware, we need to be able to handle writes not byte-by-
|
||||
* byte, but packet-by-packet. Unfortunately, that decision also exposes
|
||||
* some internals of the serial driver in the following.
|
||||
* Check if tx buf is ready or not.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static uint16_t cdcacm_fillrequest(FAR struct cdcacm_dev_s *priv,
|
||||
FAR uint8_t *reqbuf,
|
||||
uint16_t reqlen)
|
||||
static bool cdcuart_txready(FAR struct uart_dev_s *dev)
|
||||
{
|
||||
FAR uart_dev_t *serdev = &priv->serdev;
|
||||
FAR struct uart_buffer_s *xmit = &serdev->xmit;
|
||||
irqstate_t flags;
|
||||
uint16_t nbytes = 0;
|
||||
FAR struct cdcacm_dev_s *priv = dev->priv;
|
||||
|
||||
/* Disable interrupts */
|
||||
return !sq_empty(&priv->txfree);
|
||||
}
|
||||
|
||||
flags = enter_critical_section();
|
||||
/****************************************************************************
|
||||
* Name: cdcuart_sendbuf
|
||||
*
|
||||
* Description:
|
||||
* This function transfers the TX data into the request, and submits the
|
||||
* requests to the USB controller.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* Transfer bytes while we have bytes available and there is room in the
|
||||
* request.
|
||||
*/
|
||||
static ssize_t cdcuart_sendbuf(FAR struct uart_dev_s *dev,
|
||||
FAR const void *buf, size_t len)
|
||||
{
|
||||
FAR struct cdcacm_dev_s *priv = dev->priv;
|
||||
FAR struct usbdev_ep_s *ep = priv->epbulkin;
|
||||
FAR struct cdcacm_wrreq_s *wrcontainer;
|
||||
FAR struct usbdev_req_s *req;
|
||||
size_t reqlen;
|
||||
size_t nbytes;
|
||||
int ret;
|
||||
|
||||
while (xmit->head != xmit->tail && nbytes < reqlen)
|
||||
/* Get the maximum number of bytes that will fit into one bulk IN request */
|
||||
|
||||
reqlen = MAX(CONFIG_CDCACM_BULKIN_REQLEN, ep->maxpacket);
|
||||
|
||||
/* Peek at the request in the container at the head of the list */
|
||||
|
||||
wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree);
|
||||
req = wrcontainer->req;
|
||||
priv->nwrq--;
|
||||
|
||||
/* Fill the request with serial TX data */
|
||||
|
||||
nbytes = MIN(reqlen, len);
|
||||
memcpy(req->buf, buf, nbytes);
|
||||
|
||||
/* Submit the request to the endpoint */
|
||||
|
||||
req->len = nbytes;
|
||||
req->priv = wrcontainer;
|
||||
req->flags = USBDEV_REQFLAGS_NULLPKT;
|
||||
ret = EP_SUBMIT(ep, req);
|
||||
if (ret < 0)
|
||||
{
|
||||
*reqbuf++ = xmit->buffer[xmit->tail];
|
||||
nbytes++;
|
||||
|
||||
/* Increment the tail pointer */
|
||||
|
||||
if (++(xmit->tail) >= xmit->size)
|
||||
{
|
||||
xmit->tail = 0;
|
||||
}
|
||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SUBMITFAIL),
|
||||
(uint16_t)-ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* When all of the characters have been sent from the buffer disable the
|
||||
* "TX interrupt".
|
||||
*/
|
||||
|
||||
if (xmit->head == xmit->tail)
|
||||
{
|
||||
uart_disabletxint(serdev);
|
||||
}
|
||||
|
||||
/* If any bytes were removed from the buffer, inform any waiters that
|
||||
* there is space available.
|
||||
*/
|
||||
|
||||
if (nbytes)
|
||||
{
|
||||
uart_datasent(serdev);
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
|
|
@ -362,13 +365,7 @@ static uint16_t cdcacm_fillrequest(FAR struct cdcacm_dev_s *priv,
|
|||
|
||||
static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
|
||||
{
|
||||
FAR struct usbdev_ep_s *ep;
|
||||
FAR struct usbdev_req_s *req;
|
||||
FAR struct cdcacm_wrreq_s *wrcontainer;
|
||||
uint16_t reqlen;
|
||||
irqstate_t flags;
|
||||
int len;
|
||||
int ret = OK;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FEATURES
|
||||
if (priv == NULL)
|
||||
|
|
@ -379,227 +376,93 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
|
|||
#endif
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Use our bulk IN endpoint for the transfer */
|
||||
|
||||
ep = priv->epbulkin;
|
||||
|
||||
/* Loop until either (1) we run out or write requests, or (2)
|
||||
* cdcacm_fillrequest() is unable to fill the request with data (i.e.,
|
||||
* until there is no more data to be sent).
|
||||
*/
|
||||
if (priv->issending)
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->issending = true;
|
||||
}
|
||||
|
||||
uinfo("head=%d tail=%d nwrq=%d empty=%d\n",
|
||||
priv->serdev.xmit.head, priv->serdev.xmit.tail,
|
||||
priv->nwrq, sq_empty(&priv->txfree));
|
||||
|
||||
/* Get the maximum number of bytes that will fit into one bulk IN request */
|
||||
uart_xmitchars(&priv->serdev);
|
||||
|
||||
reqlen = MAX(CONFIG_CDCACM_BULKIN_REQLEN, ep->maxpacket);
|
||||
|
||||
while (!sq_empty(&priv->txfree))
|
||||
{
|
||||
/* Peek at the request in the container at the head of the list */
|
||||
|
||||
wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_peek(&priv->txfree);
|
||||
req = wrcontainer->req;
|
||||
|
||||
/* Fill the request with serial TX data */
|
||||
|
||||
len = cdcacm_fillrequest(priv, req->buf, reqlen);
|
||||
if (len > 0)
|
||||
{
|
||||
/* Remove the empty container from the request list */
|
||||
|
||||
sq_remfirst(&priv->txfree);
|
||||
priv->nwrq--;
|
||||
|
||||
/* Then submit the request to the endpoint */
|
||||
|
||||
req->len = len;
|
||||
req->priv = wrcontainer;
|
||||
req->flags = USBDEV_REQFLAGS_NULLPKT;
|
||||
ret = EP_SUBMIT(ep, req);
|
||||
if (ret != OK)
|
||||
{
|
||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SUBMITFAIL),
|
||||
(uint16_t)-ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
priv->issending = false;
|
||||
|
||||
out:
|
||||
leave_critical_section(flags);
|
||||
return ret;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: cdcacm_recvpacket
|
||||
* Name: cdcuart_rxavailable
|
||||
*
|
||||
* Description:
|
||||
* A normal completion event was received by the read completion handler
|
||||
* at the interrupt level (with interrupts disabled). This function handles
|
||||
* the USB packet and provides the received data to the uart RX buffer.
|
||||
*
|
||||
* Assumptions:
|
||||
* Called from the USB interrupt handler with interrupts disabled.
|
||||
* Check if data has been saved in rx buf.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
|
||||
FAR struct cdcacm_rdreq_s *rdcontainer)
|
||||
static bool cdcuart_rxavailable(FAR struct uart_dev_s *dev)
|
||||
{
|
||||
FAR uart_dev_t *serdev;
|
||||
FAR struct uart_buffer_s *recv;
|
||||
FAR struct cdcacm_dev_s *priv = dev->priv;
|
||||
|
||||
return !sq_empty(&priv->rxpending);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: cdcuart_recvbuf
|
||||
*
|
||||
* Description:
|
||||
* This function handles the USB packet and provides the received data to
|
||||
* the uart RX buffer.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t cdcuart_recvbuf(FAR struct uart_dev_s *dev,
|
||||
FAR void *buf, size_t len)
|
||||
{
|
||||
FAR struct cdcacm_dev_s *priv = dev->priv;
|
||||
FAR struct cdcacm_rdreq_s *rdcontainer;
|
||||
FAR struct usbdev_req_s *req;
|
||||
FAR uint8_t *reqbuf;
|
||||
#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS
|
||||
unsigned int watermark;
|
||||
#endif
|
||||
uint16_t reqlen;
|
||||
uint16_t nexthead;
|
||||
uint16_t nbytes = 0;
|
||||
size_t reqlen;
|
||||
size_t nbytes;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(priv != NULL && rdcontainer != NULL);
|
||||
/* Process each packet in the priv->rxpending list */
|
||||
|
||||
#ifdef CONFIG_CDCACM_IFLOWCONTROL
|
||||
DEBUGASSERT(priv->rxenabled && !priv->iactive);
|
||||
#else
|
||||
DEBUGASSERT(priv->rxenabled);
|
||||
#endif
|
||||
rdcontainer = (FAR struct cdcacm_rdreq_s *)sq_peek(&priv->rxpending);
|
||||
DEBUGASSERT(rdcontainer != NULL);
|
||||
|
||||
req = rdcontainer->req;
|
||||
req = rdcontainer->req;
|
||||
DEBUGASSERT(req != NULL);
|
||||
|
||||
reqbuf = &req->buf[rdcontainer->offset];
|
||||
reqlen = req->xfrd - rdcontainer->offset;
|
||||
|
||||
uinfo("head=%d tail=%d nrdq=%d reqlen=%d\n",
|
||||
priv->serdev.recv.head, priv->serdev.recv.tail, priv->nrdq, reqlen);
|
||||
nbytes = MIN(reqlen, len);
|
||||
memcpy(buf, reqbuf, nbytes);
|
||||
rdcontainer->offset += nbytes;
|
||||
|
||||
serdev = &priv->serdev;
|
||||
recv = &serdev->recv;
|
||||
|
||||
/* 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
|
||||
* BEFORE we overrun the buffer!
|
||||
/* The entire packet was processed and may be removed from the
|
||||
* pending RX list.
|
||||
*/
|
||||
|
||||
nexthead = recv->head + 1;
|
||||
if (nexthead >= recv->size)
|
||||
if (rdcontainer->offset >= req->xfrd)
|
||||
{
|
||||
nexthead = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS
|
||||
/* Pre-calculate the watermark level that we will need to test against.
|
||||
* Note that the range of the the upper watermark is from 1 to 99 percent
|
||||
* and that the actual capacity of the RX buffer is (recv->size - 1).
|
||||
*/
|
||||
|
||||
watermark = CONFIG_SERIAL_IFLOWCONTROL_UPPER_WATERMARK * recv->size / 100;
|
||||
DEBUGASSERT(watermark > 0 && watermark < (recv->size - 1));
|
||||
#endif
|
||||
|
||||
/* Then copy data into the RX buffer until either: (1) all of the data has
|
||||
* been copied, or (2) the RX buffer is full.
|
||||
*
|
||||
* NOTE: If the RX buffer becomes full, then we have overrun the serial
|
||||
* driver and data will be lost. This is the correct behavior for a
|
||||
* proper emulation of a serial link. It should not NAK, it should drop
|
||||
* data like a physical serial port.
|
||||
*
|
||||
* If you don't like that behavior. DO NOT change it here. Instead, you
|
||||
* should finish the implementation of RX flow control which is the only
|
||||
* proper way to throttle a serial device.
|
||||
*/
|
||||
|
||||
while (nexthead != recv->tail && nbytes < reqlen)
|
||||
{
|
||||
#if defined(CONFIG_SERIAL_IFLOWCONTROL) && \
|
||||
defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS)
|
||||
unsigned int nbuffered;
|
||||
|
||||
/* How many bytes are buffered */
|
||||
|
||||
if (recv->head >= recv->tail)
|
||||
sq_remfirst(&priv->rxpending);
|
||||
ret = cdcacm_requeue_rdrequest(priv, rdcontainer);
|
||||
if (ret < 0)
|
||||
{
|
||||
nbuffered = recv->head - recv->tail;
|
||||
}
|
||||
else
|
||||
{
|
||||
nbuffered = recv->size - recv->tail + recv->head;
|
||||
}
|
||||
|
||||
/* Is the level now above the watermark level that we need to report? */
|
||||
|
||||
if (nbuffered >= watermark)
|
||||
{
|
||||
/* Let the lower level driver know that the watermark level has
|
||||
* been crossed. It will probably activate RX flow control.
|
||||
*/
|
||||
|
||||
if (cdcuart_rxflowcontrol(&priv->serdev, nbuffered, true))
|
||||
{
|
||||
/* Low-level driver activated RX flow control, exit loop now. */
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Copy one byte to the head of the circular RX buffer */
|
||||
|
||||
recv->buffer[recv->head] = *reqbuf++;
|
||||
nbytes++;
|
||||
|
||||
/* Increment the head index and check for wrap around */
|
||||
|
||||
recv->head = nexthead;
|
||||
if (++nexthead >= recv->size)
|
||||
{
|
||||
nexthead = 0;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SERIAL_IFLOWCONTROL) && \
|
||||
!defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS)
|
||||
/* Check if RX buffer became full and allow serial low-level driver to
|
||||
* pause processing. This allows proper utilization of hardware flow
|
||||
* control when there are no watermarks.
|
||||
*/
|
||||
|
||||
if (nexthead == recv->tail)
|
||||
{
|
||||
cdcuart_rxflowcontrol(&priv->serdev, recv->size - 1, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
* handler, then the serial driver will not run until the interrupt
|
||||
* handler returns.
|
||||
*/
|
||||
|
||||
if (nbytes > 0)
|
||||
{
|
||||
uart_datareceived(serdev);
|
||||
}
|
||||
|
||||
/* Return an overrun error if the entire packet could not be transferred. */
|
||||
|
||||
if (nbytes < reqlen)
|
||||
{
|
||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RXOVERRUN), 0);
|
||||
rdcontainer->offset += nbytes;
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return OK;
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -647,7 +510,6 @@ static int cdcacm_requeue_rdrequest(FAR struct cdcacm_dev_s *priv,
|
|||
|
||||
static int cdcacm_release_rxpending(FAR struct cdcacm_dev_s *priv)
|
||||
{
|
||||
FAR struct cdcacm_rdreq_s *rdcontainer;
|
||||
irqstate_t flags;
|
||||
int ret = -EBUSY;
|
||||
|
||||
|
|
@ -683,34 +545,7 @@ static int cdcacm_release_rxpending(FAR struct cdcacm_dev_s *priv)
|
|||
|
||||
ret = OK;
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
sq_remfirst(&priv->rxpending);
|
||||
ret = cdcacm_requeue_rdrequest(priv, rdcontainer);
|
||||
}
|
||||
uart_recvchars(&priv->serdev);
|
||||
}
|
||||
|
||||
/* Restart the RX failsafe timer if there are RX packets in
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue