drivers/usbdev: Add support for flow control TERMIOs in CDC/ACM driver

This commit is contained in:
Gregory Nutt 2017-09-23 11:00:26 -06:00
parent b065b1f5df
commit 5d02baf205
1 changed files with 110 additions and 25 deletions

View File

@ -85,8 +85,8 @@ struct cdcacm_req_s
struct cdcacm_dev_s
{
FAR struct uart_dev_s serdev; /* Serial device structure */
FAR struct usbdev_s *usbdev; /* usbdev driver pointer */
FAR struct uart_dev_s serdev; /* Serial device structure */
FAR struct usbdev_s *usbdev; /* usbdev driver pointer */
uint8_t config; /* Configuration number */
uint8_t nwrq; /* Number of queue write requests (in reqlist) */
@ -94,19 +94,21 @@ struct cdcacm_dev_s
uint8_t minor; /* The device minor number */
#ifdef CONFIG_CDCACM_IFLOWCONTROL
uint8_t serialstate; /* State of the DSR/DCD */
bool iflow; /* True: input flow control is enabled */
bool upper; /* True: RX buffer is (nearly) full */
#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 */
cdcacm_callback_t callback; /* Serial event callback function */
uint8_t ctrlline; /* Buffered control line state */
struct cdc_linecoding_s linecoding; /* Buffered line status */
cdcacm_callback_t callback; /* Serial event callback function */
FAR struct usbdev_ep_s *epintin; /* Interrupt 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 *epintin; /* Interrupt 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_req_s *ctrlreq; /* Allocated control request */
struct sq_queue_s reqlist; /* List of write request containers */
struct sq_queue_s reqlist; /* List of write request containers */
struct usbdev_devinfo_s devinfo;
@ -2106,12 +2108,26 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
termiosp->c_iflag = serdev->tc_iflag;
termiosp->c_oflag = serdev->tc_oflag;
termiosp->c_lflag = serdev->tc_lflag;
termiosp->c_cflag = CS8;
#ifdef CONFIG_CDCACM_OFLOWCONTROL
/* Report state of output flow control */
# warning Missing logic
#endif
#ifdef CONFIG_CDCACM_IFLOWCONTROL
/* Report state of input flow control */
termiosp->c_lflag = (priv->iflow) ? CRTS_IFLOW : 0;
#endif
}
break;
case TCSETS:
{
struct termios *termiosp = (FAR struct termios *)arg;
#ifdef CONFIG_CDCACM_IFLOWCONTROL
bool iflow;
#endif
if (!termiosp)
{
@ -2124,6 +2140,46 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
serdev->tc_iflag = termiosp->c_iflag;
serdev->tc_oflag = termiosp->c_oflag;
serdev->tc_lflag = termiosp->c_lflag;
#ifdef CONFIG_CDCACM_OFLOWCONTROL
/* Handle changes to output flow control */
# warning Missing logic
#endif
#ifdef CONFIG_CDCACM_IFLOWCONTROL
/* Handle changes to input flow control */
iflow = ((termiosp->c_cflag & CRTS_IFLOW) != 0);
if (iflow != priv->iflow)
{
/* The input flow control state has changed. Save the new
* flow control setting.
*/
priv->iflow = iflow;
/* If flow control has been disabled, then we will need to
* make sure that DSR is set.
*/
if (!iflow && (priv->serialstate & CDCACM_UART_DSR) == 0)
{
priv->serialstate |= (CDCACM_UART_DSR | CDCACM_UART_DCD);
ret = cdcacm_serialstate(priv);
}
/* If flow control has been enabled and the RX buffer is already
* (nearly) full, the we need to make sure the DSR is clear.
*/
else if (priv->upper)
{
priv->serialstate &= ~CDCACM_UART_DSR;
priv->serialstate |= CDCACM_UART_DCD;
ret = cdcacm_serialstate(priv);
}
}
#endif
}
break;
#endif
@ -2339,6 +2395,7 @@ static bool cdcuart_rxflowcontrol(FAR struct uart_dev_s *dev,
{
#ifdef CONFIG_CDCACM_IFLOWCONTROL
FAR struct cdcacm_dev_s *priv;
uint8_t mask;
int ret;
/* Sanity check */
@ -2355,27 +2412,55 @@ static bool cdcuart_rxflowcontrol(FAR struct uart_dev_s *dev,
priv = (FAR struct cdcacm_dev_s *)dev->priv;
/* Set DSR (TX carrier) if the lower water mark has been crossed or clear it if the
* upper water mark has been crossed.
*/
/* Is input flow control enabled? */
if (upper)
priv->upper = upper;
if (priv->iflow)
{
priv->serialstate &= ~CDCACM_UART_DSR;
/* Yes.. Set DSR (TX carrier) if the lower water mark has been crossed
* or clear it if the upper water mark has been crossed.
*/
mask = upper ? 0 : CDCACM_UART_DSR;
/* Don't do anything unless this results in a change in the setting of
* DSR.
*/
if (((priv->serialstate ^ mask) & CDCACM_UART_DSR) != 0)
{
/* Set or clear DSR (set DCD in any case). */
priv->serialstate &= ~CDCACM_UART_DSR;
priv->serialstate |= (mask | CDCACM_UART_DCD);
/* And send the SerialState message */
ret = cdcacm_serialstate(priv);
return (ret >= 0 && upper);
}
/* Return true of DSR is not set */
return ((priv->serialstate & CDCACM_UART_DSR) == 0);
}
else
{
priv->serialstate |= CDCACM_UART_DSR;
/* Flow control is disabled ... DSR must be set */
if ((priv->serialstate & CDCACM_UART_DSR) == 0)
{
/* Set DSR and DCD */
priv->serialstate |= (CDCACM_UART_DSR | CDCACM_UART_DCD);
/* And send the SerialState message */
(void)cdcacm_serialstate(priv);
}
return false;
}
/* Set DCD in any event */
priv->serialstate |= CDCACM_UART_DCD;
/* And send the SerialState message */
ret = cdcacm_serialstate(priv);
return (ret >= 0 && upper);
#else
return false;