forked from nuttx/nuttx-update
Correct emulation of UART RX disabled
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@1016 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
parent
a8f3345e5a
commit
c9d3adb0b9
1 changed files with 149 additions and 43 deletions
|
@ -238,11 +238,15 @@ struct usbser_dev_s
|
|||
{
|
||||
FAR struct uart_dev_s serdev; /* Serial device structure */
|
||||
FAR struct usbdev_s *usbdev; /* usbdev driver pointer */
|
||||
ubyte config; /* Configuration number */
|
||||
ubyte nwrq; /* Number of queue write requests (in reqlist)*/
|
||||
ubyte nrdq; /* Number of queue read requests (in epbulkout) */
|
||||
boolean open; /* TRUE: Driver has been opened */
|
||||
ubyte linest[7]; /* Fake line status */
|
||||
|
||||
ubyte config; /* Configuration number */
|
||||
ubyte nwrq; /* Number of queue write requests (in reqlist)*/
|
||||
ubyte nrdq; /* Number of queue read requests (in epbulkout) */
|
||||
ubyte open : 1; /* 1: Driver has been opened */
|
||||
ubyte rxenabled : 1; /* 1: UART RX "interrupts" enabled */
|
||||
ubyte linest[7]; /* Fake line status */
|
||||
sint16 rxhead; /* Working head; used when rx int disabled */
|
||||
|
||||
FAR struct usbdev_ep_s *epintin; /* Address of Interrupt IN endpoint */
|
||||
FAR struct usbdev_ep_s *epbulkin; /* Address of Bulk IN endpoint */
|
||||
FAR struct usbdev_ep_s *epbulkout; /* Address of Bulk OUT endpoint */
|
||||
|
@ -288,7 +292,7 @@ struct usbser_alloc_s
|
|||
static uint16 usbclass_fillpacket(FAR struct usbser_dev_s *priv,
|
||||
char *packet, uint16 size);
|
||||
static int usbclass_sndpacket(FAR struct usbser_dev_s *priv);
|
||||
static int usbclass_recvpacket(FAR struct usbser_dev_s *priv,
|
||||
static inline int usbclass_recvpacket(FAR struct usbser_dev_s *priv,
|
||||
char *packet, uint16 size);
|
||||
|
||||
/* Request helpers *********************************************************/
|
||||
|
@ -314,11 +318,11 @@ static int usbclass_setconfig(FAR struct usbser_dev_s *priv,
|
|||
|
||||
/* Completion event handlers ***********************************************/
|
||||
|
||||
static void usbclass_setupcomplete(FAR struct usbdev_ep_s *ep,
|
||||
static void usbclass_ep0incomplete(FAR struct usbdev_ep_s *ep,
|
||||
FAR struct usbdev_req_s *req);
|
||||
static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep,
|
||||
static void usbclass_epbulkoutcomplete(FAR struct usbdev_ep_s *ep,
|
||||
FAR struct usbdev_req_s *req);
|
||||
static void usbclass_wrcomplete(FAR struct usbdev_ep_s *ep,
|
||||
static void usbclass_epbulkincomplete(FAR struct usbdev_ep_s *ep,
|
||||
FAR struct usbdev_req_s *req);
|
||||
|
||||
/* USB class device ********************************************************/
|
||||
|
@ -624,20 +628,33 @@ static int usbclass_sndpacket(FAR struct usbser_dev_s *priv)
|
|||
* 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.
|
||||
*
|
||||
************************************************************************************/
|
||||
|
||||
static int usbclass_recvpacket(FAR struct usbser_dev_s *priv, char *packet, uint16 size)
|
||||
static inline int usbclass_recvpacket(FAR struct usbser_dev_s *priv,
|
||||
char *packet, uint16 size)
|
||||
{
|
||||
FAR uart_dev_t *serdev = &priv->serdev;
|
||||
FAR struct uart_buffer_s *recv = &serdev->recv;
|
||||
uint16 currhead;
|
||||
uint16 nexthead;
|
||||
|
||||
/* Get the next head index */
|
||||
/* Get the next head index. 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; Instead we will
|
||||
* use a shadow copy of the index. When interrupts are restored, the real recv.head
|
||||
* will be updated with this indes.
|
||||
*/
|
||||
|
||||
nexthead = recv->head + 1;
|
||||
if (nexthead >= recv->size)
|
||||
if (priv->rxenabled)
|
||||
{
|
||||
nexthead = 0;
|
||||
currhead = recv->head;
|
||||
}
|
||||
else
|
||||
{
|
||||
currhead = priv->rxhead;
|
||||
}
|
||||
|
||||
/* Then copy data into the RX buffer until either: (1) all of the data has been
|
||||
|
@ -647,27 +664,61 @@ static int usbclass_recvpacket(FAR struct usbser_dev_s *priv, char *packet, uint
|
|||
|
||||
while (nexthead != recv->tail && size > 0)
|
||||
{
|
||||
/* Pre-increment 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!
|
||||
*/
|
||||
|
||||
nexthead = currhead + 1;
|
||||
if (nexthead >= recv->size)
|
||||
{
|
||||
nexthead = 0;
|
||||
}
|
||||
|
||||
/* Copy one byte to the head of the circular RX buffer */
|
||||
|
||||
recv->buffer[recv->head] = *packet++;
|
||||
recv->buffer[currhead] = *packet++;
|
||||
|
||||
/* Update counts and indices */
|
||||
|
||||
recv->head = nexthead;
|
||||
currhead = nexthead;
|
||||
size--;
|
||||
if (++nexthead >= recv->size)
|
||||
{
|
||||
nexthead = 0;
|
||||
}
|
||||
|
||||
/* Wake up the serial driver if it is waiting for incoming data */
|
||||
/* Wake up the serial driver if it 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. But we will exercise
|
||||
* care in the following just in case the serial driver does run.
|
||||
*/
|
||||
|
||||
if (serdev->recvwaiting)
|
||||
if (priv->rxenabled && serdev->recvwaiting)
|
||||
{
|
||||
recv->head = currhead;
|
||||
serdev->recvwaiting = FALSE;
|
||||
sem_post(&serdev->recvsem);
|
||||
currhead = recv->head;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write back the head pointer using the shadow index if RX "interrupts"
|
||||
* are disabled.
|
||||
*/
|
||||
|
||||
if (priv->rxenabled)
|
||||
{
|
||||
recv->head = currhead;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->rxhead = currhead;
|
||||
}
|
||||
|
||||
/* Return an error if the entire packet could not be transferred */
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RXOVERRUN), 0);
|
||||
return -ENOSPC;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -1024,7 +1075,7 @@ static int usbclass_setconfig(FAR struct usbser_dev_s *priv, ubyte config)
|
|||
for (i = 0; i < CONFIG_USBSER_NRDREQS; i++)
|
||||
{
|
||||
req = priv->rdreqs[i].req;
|
||||
req->callback = usbclass_rdcomplete;
|
||||
req->callback = usbclass_epbulkoutcomplete;
|
||||
ret = EP_SUBMIT(priv->epbulkout, req);
|
||||
if (ret != OK)
|
||||
{
|
||||
|
@ -1043,14 +1094,14 @@ errout:
|
|||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: usbclass_setupcomplete
|
||||
* Name: usbclass_ep0incomplete
|
||||
*
|
||||
* Description:
|
||||
* Handle completion of EP0 control operations
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void usbclass_setupcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req)
|
||||
static void usbclass_ep0incomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req)
|
||||
{
|
||||
if (req->result || req->xfrd != req->len)
|
||||
{
|
||||
|
@ -1059,16 +1110,17 @@ static void usbclass_setupcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req
|
|||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: usbclass_rdcomplete
|
||||
* Name: usbclass_epbulkoutcomplete
|
||||
*
|
||||
* Description:
|
||||
* Handle completion of read request
|
||||
*
|
||||
* Handle completion of read request on the bulk OUT endpoint. This
|
||||
* is handled like the receipt of serial data on the "UART"
|
||||
****************************************************************************/
|
||||
|
||||
static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req)
|
||||
static void usbclass_epbulkoutcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req)
|
||||
{
|
||||
FAR struct usbser_dev_s *priv;
|
||||
irqstate_t flags;
|
||||
int ret;
|
||||
|
||||
/* Sanity check */
|
||||
|
@ -1087,6 +1139,7 @@ static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s
|
|||
|
||||
/* Process the received data unless this is some unusual condition */
|
||||
|
||||
flags = irqsave();
|
||||
switch (req->result)
|
||||
{
|
||||
case 0: /* Normal completion */
|
||||
|
@ -1097,6 +1150,7 @@ static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s
|
|||
case -ESHUTDOWN: /* Disconnection */
|
||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0);
|
||||
priv->nrdq--;
|
||||
irqrestore(flags);
|
||||
return;
|
||||
|
||||
default: /* Some other error occurred */
|
||||
|
@ -1112,10 +1166,11 @@ static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s
|
|||
{
|
||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16)-req->result);
|
||||
}
|
||||
irqrestore(flags);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: usbclass_wrcomplete
|
||||
* Name: usbclass_epbulkincomplete
|
||||
*
|
||||
* Description:
|
||||
* Handle completion of write request. This function probably executes
|
||||
|
@ -1123,7 +1178,7 @@ static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void usbclass_wrcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req)
|
||||
static void usbclass_epbulkincomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req)
|
||||
{
|
||||
FAR struct usbser_dev_s *priv;
|
||||
FAR struct usbser_req_s *reqcontainer;
|
||||
|
@ -1208,7 +1263,7 @@ static int usbclass_bind(FAR struct usbdev_s *dev, FAR struct usbdevclass_driver
|
|||
ret = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
priv->ctrlreq->callback = usbclass_setupcomplete;
|
||||
priv->ctrlreq->callback = usbclass_ep0incomplete;
|
||||
|
||||
/* Pre-allocate all endpoints... the endpoints will not be functional
|
||||
* until the SET CONFIGURATION request is processed in usbclass_setconfig.
|
||||
|
@ -1263,7 +1318,7 @@ static int usbclass_bind(FAR struct usbdev_s *dev, FAR struct usbdevclass_driver
|
|||
goto errout;
|
||||
}
|
||||
reqcontainer->req->private = reqcontainer;
|
||||
reqcontainer->req->callback = usbclass_rdcomplete;
|
||||
reqcontainer->req->callback = usbclass_epbulkoutcomplete;
|
||||
}
|
||||
|
||||
/* Pre-allocate write request containers and put in a free list */
|
||||
|
@ -1279,7 +1334,7 @@ static int usbclass_bind(FAR struct usbdev_s *dev, FAR struct usbdevclass_driver
|
|||
goto errout;
|
||||
}
|
||||
reqcontainer->req->private = reqcontainer;
|
||||
reqcontainer->req->callback = usbclass_wrcomplete;
|
||||
reqcontainer->req->callback = usbclass_epbulkincomplete;
|
||||
|
||||
flags = irqsave();
|
||||
sq_addlast((sq_entry_t*)reqcontainer, &priv->reqlist);
|
||||
|
@ -1664,7 +1719,7 @@ static int usbclass_setup(FAR struct usbdev_s *dev, const struct usb_ctrlreq_s *
|
|||
{
|
||||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPRESPQ), (uint16)-ret);
|
||||
ctrlreq->result = OK;
|
||||
usbclass_setupcomplete(dev->ep0, ctrlreq);
|
||||
usbclass_ep0incomplete(dev->ep0, ctrlreq);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1762,7 +1817,7 @@ static int usbser_setup(FAR struct uart_dev_s *dev)
|
|||
|
||||
/* Mark the device as opened */
|
||||
|
||||
priv->open = TRUE;
|
||||
priv->open = 1;
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -1812,7 +1867,7 @@ static void usbser_shutdown(FAR struct uart_dev_s *dev)
|
|||
/* Make sure that we are disconnected from the host */
|
||||
|
||||
usbclass_resetconfig(priv);
|
||||
priv->open = FALSE;
|
||||
priv->open = 0;
|
||||
irqrestore(flags);
|
||||
}
|
||||
|
||||
|
@ -1863,6 +1918,8 @@ static void usbser_detach(FAR struct uart_dev_s *dev)
|
|||
static void usbser_rxint(FAR struct uart_dev_s *dev, boolean enable)
|
||||
{
|
||||
FAR struct usbser_dev_s *priv;
|
||||
FAR uart_dev_t *serdev;
|
||||
irqstate_t flags;
|
||||
|
||||
usbtrace(USBSER_CLASSAPI_RXINT, (uint16)enable);
|
||||
|
||||
|
@ -1878,20 +1935,69 @@ static void usbser_rxint(FAR struct uart_dev_s *dev, boolean enable)
|
|||
|
||||
/* Extract reference to private data */
|
||||
|
||||
priv = (FAR struct usbser_dev_s*)dev->priv;
|
||||
priv = (FAR struct usbser_dev_s*)dev->priv;
|
||||
serdev = &priv->serdev;
|
||||
|
||||
/* I think we can simulate this behavior by stalling and/or resuming the
|
||||
* OUT endpoint.
|
||||
/* We need exclusive access to the RX buffer and private structure
|
||||
* in the following.
|
||||
*/
|
||||
|
||||
flags = irqsave();
|
||||
if (enable)
|
||||
{
|
||||
EP_RESUME(priv->epbulkout);
|
||||
/* RX "interrupts" are enabled. Is this a transition from disabled
|
||||
* to enabled state?
|
||||
*/
|
||||
|
||||
if (!priv->rxenabled)
|
||||
{
|
||||
/* 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 restored,
|
||||
* we can update the head pointer for all of the data that we
|
||||
* put into cicular buffer while "interrupts" were disabled.
|
||||
*/
|
||||
|
||||
if (priv->rxhead != serdev->recv.head)
|
||||
{
|
||||
serdev->recv.head = priv->rxhead;
|
||||
|
||||
/* Is the serial driver waiting for more data? */
|
||||
|
||||
if (serdev->recvwaiting)
|
||||
{
|
||||
/* Yes... signal the availability of new data */
|
||||
|
||||
sem_post(&serdev->recvsem);
|
||||
serdev->recvwaiting = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* RX "interrupts are no longer disabled */
|
||||
|
||||
priv->rxenabled = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
/* RX "interrupts" are disabled. Is this a transition from enabled
|
||||
* to disabled state?
|
||||
*/
|
||||
|
||||
else if (priv->rxenabled)
|
||||
{
|
||||
EP_STALL(priv->epbulkout);
|
||||
/* 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 = 0;
|
||||
}
|
||||
irqrestore(flags);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
|
Loading…
Reference in a new issue