diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-08-10 19:14:05 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-08-10 19:14:05 -0600 |
commit | 3c5fcd7ca22c13071fc85c89036adf43f92ebaf2 (patch) | |
tree | 622e43facf3bd417459d2681c47bab360d4a33e1 | |
parent | 9a16be559048f93c3396183b1dc1261781d54b01 (diff) | |
download | px4-nuttx-3c5fcd7ca22c13071fc85c89036adf43f92ebaf2.tar.gz px4-nuttx-3c5fcd7ca22c13071fc85c89036adf43f92ebaf2.tar.bz2 px4-nuttx-3c5fcd7ca22c13071fc85c89036adf43f92ebaf2.zip |
Serial FIONREAD, FIONWRITE, and TERMIOS I/O processing from Mike Smith, Andrew Tridgell, and and Lorenz Meier
-rw-r--r-- | nuttx/ChangeLog | 6 | ||||
-rw-r--r-- | nuttx/drivers/mmcsd/mmcsd_sdio.c | 4 | ||||
-rw-r--r-- | nuttx/drivers/serial/serial.c | 220 | ||||
-rw-r--r-- | nuttx/drivers/usbdev/cdcacm.c | 47 | ||||
-rw-r--r-- | nuttx/include/nuttx/fs/ioctl.h | 7 |
5 files changed, 268 insertions, 16 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index 691f3dcc3..7fc5d0a04 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -5342,4 +5342,8 @@ * drivers/mmcsd/mmcsd_sdio.c: Correction for a bad return value when multiple block SDIO transfers are suppressed. By Andrew Tridgell via Lorenz Meier (2013-8-10). - + * drivers/serial/serial.c, drivers/usbdev/cdcacm.c, and + include/nuttx/fs/ioctl.h: Added support for FIONREAD and FIONWRITE, + added TERMIOS input / output processing support for UART and CDCACM + serial ports. Implemented by Mike Smith, Andrew Tridgell and Lorenz + Meier (2013-8-10). diff --git a/nuttx/drivers/mmcsd/mmcsd_sdio.c b/nuttx/drivers/mmcsd/mmcsd_sdio.c index 4d39e847a..6c9c01660 100644 --- a/nuttx/drivers/mmcsd/mmcsd_sdio.c +++ b/nuttx/drivers/mmcsd/mmcsd_sdio.c @@ -1821,7 +1821,7 @@ static ssize_t mmcsd_flush(FAR void *dev, FAR const uint8_t *buffer, size_t block; size_t endblock; #endif - ssize_t ret = nblocks; + ssize_t ret; DEBUGASSERT(priv != NULL && buffer != NULL && nblocks > 0) @@ -1829,6 +1829,8 @@ static ssize_t mmcsd_flush(FAR void *dev, FAR const uint8_t *buffer, /* Write each block using only the single block transfer method */ endblock = startblock + nblocks - 1; + ret = nblocks; + for (block = startblock; block <= endblock; block++) { /* Write this block from the user buffer */ diff --git a/nuttx/drivers/serial/serial.c b/nuttx/drivers/serial/serial.c index 912feb255..690ae2efd 100644 --- a/nuttx/drivers/serial/serial.c +++ b/nuttx/drivers/serial/serial.c @@ -54,6 +54,7 @@ #include <nuttx/arch.h> #include <nuttx/fs/fs.h> #include <nuttx/serial/serial.h> +#include <nuttx/fs/ioctl.h> /************************************************************************************ * Definitions @@ -187,7 +188,7 @@ static int uart_putxmitchar(FAR uart_dev_t *dev, int ch, bool oktoblock) /* Increment to see what the next head pointer will be. We need to use the "next" * head pointer to determine when the circular buffer would overrun */ - + nexthead = dev->xmit.head + 1; if (nexthead >= dev->xmit.size) { @@ -195,7 +196,7 @@ static int uart_putxmitchar(FAR uart_dev_t *dev, int ch, bool oktoblock) } /* Loop until we are able to add the character to the TX buffer */ - + for (;;) { if (nexthead != dev->xmit.tail) @@ -317,13 +318,15 @@ static inline ssize_t uart_irqwrite(FAR uart_dev_t *dev, FAR const char *buffer, * Name: uart_write ************************************************************************************/ -static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) +static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) { FAR struct inode *inode = filep->f_inode; FAR uart_dev_t *dev = inode->i_private; ssize_t nread = buflen; bool oktoblock; int ret; + char ch; /* We may receive console writes through this path from interrupt handlers and * from debug output in the IDLE task! In these cases, we will need to do things @@ -401,16 +404,51 @@ static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, size_t uart_disabletxint(dev); for (; buflen; buflen--) { - int ch = *buffer++; + ch = *buffer++; + ret = OK; - /* If this is the console, then we should replace LF with CR-LF */ +#ifdef CONFIG_SERIAL_TERMIOS + /* Do output post-processing */ + + if (dev->tc_oflag & OPOST) + { + /* Mapping CR to NL? */ + + if ((ch == '\r') && (dev->tc_oflag & OCRNL)) + { + ch = '\n'; + } + + /* Are we interested in newline processing? */ + + if ((ch == '\n') && (dev->tc_oflag & (ONLCR | ONLRET))) + { + ret = uart_putxmitchar(dev, '\r', oktoblock); + if (ret < 0) + { + break; + } + } + + /* Specifically not handled: + * + * OXTABS - primarily a full-screen terminal optimisation + * ONOEOT - Unix interoperability hack + * OLCUC - Not specified by Posix + * ONOCR - low-speed interactive optimisation + */ + } + +#else /* !CONFIG_SERIAL_TERMIOS */ + /* If this is the console, convert \n -> \r\n */ - ret = OK; if (dev->isconsole && ch == '\n') { ret = uart_putxmitchar(dev, '\r', oktoblock); } +#endif + /* Put the character into the transmit buffer */ if (ret == OK) @@ -477,6 +515,7 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen ssize_t recvd = 0; int16_t tail; int ret; + char ch; /* Only one user can access dev->recv.tail at a time */ @@ -530,8 +569,7 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen { /* Take the next character from the tail of the buffer */ - *buffer++ = dev->recv.buffer[tail]; - recvd++; + ch = dev->recv.buffer[tail]; /* Increment the tail index. Most operations are done using the * local variable 'tail' so that the final dev->recv.tail update @@ -544,6 +582,45 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen } dev->recv.tail = tail; + +#ifdef CONFIG_SERIAL_TERMIOS + /* Do input processing if any is enabled */ + + if (dev->tc_iflag & (INLCR | IGNCR | ICRNL)) + { + /* \n -> \r or \r -> \n translation? */ + + if ((ch == '\n') && (dev->tc_iflag & INLCR)) + { + ch = '\r'; + } + else if ((ch == '\r') && (dev->tc_iflag & ICRNL)) + { + ch = '\n'; + } + + /* Discarding \r ? */ + + if ((ch == '\r') & (dev->tc_iflag & IGNCR)) + { + continue; + } + } + + /* Specifically not handled: + * + * All of the local modes; echo, line editing, etc. + * Anything to do with break or parity errors. + * ISTRIP - we should be 8-bit clean. + * IUCLC - Not Posix + * IXON/OXOFF - no xon/xoff flow control. + */ +#endif + + /* Store the received character */ + + *buffer++ = ch; + recvd++; } #ifdef CONFIG_DEV_SERIAL_FULLBLOCKS @@ -698,7 +775,114 @@ static int uart_ioctl(FAR struct file *filep, int cmd, unsigned long arg) FAR struct inode *inode = filep->f_inode; FAR uart_dev_t *dev = inode->i_private; - return dev->ops->ioctl(filep, cmd, arg); + /* Handle TTY-level IOCTLs here */ + /* Let low-level driver handle the call first */ + + int ret = dev->ops->ioctl(filep, cmd, arg); + + /* The device ioctl() handler returns -ENOTTY when it doesn't know + * how to handle the command. Check if we can handle it here. + */ + + if (ret == -ENOTTY) + { + switch (cmd) + { + case FIONREAD: + { + int count; + irqstate_t state = irqsave(); + + /* Determine the number of bytes available in the buffer */ + + if (dev->recv.tail <= dev->recv.head) + { + count = dev->recv.head - dev->recv.tail; + } + else + { + count = dev->recv.size - (dev->recv.tail - dev->recv.head); + } + + irqrestore(state); + + *(int *)arg = count; + ret = 0; + } + break; + + case FIONWRITE: + { + int count; + irqstate_t state = irqsave(); + + /* Determine the number of bytes free in the buffer */ + + if (dev->xmit.head < dev->xmit.tail) + { + count = dev->xmit.tail - dev->xmit.head - 1; + } + else + { + count = dev->xmit.size - (dev->xmit.head - dev->xmit.tail) - 1; + } + + irqrestore(state); + + *(int *)arg = count; + ret = 0; + } + break; + } + } + +#ifdef CONFIG_SERIAL_TERMIOS + /* Append any higher level TTY flags */ + + else if (ret == OK) + { + switch (cmd) + { + case TCGETS: + { + struct termios *termiosp = (struct termios*)arg; + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + /* And update with flags from this layer */ + + termiosp->c_iflag = dev->tc_iflag; + termiosp->c_oflag = dev->tc_oflag; + termiosp->c_lflag = dev->tc_lflag; + } + break; + + case TCSETS: + { + struct termios *termiosp = (struct termios*)arg; + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + /* Update the flags we keep at this layer */ + + dev->tc_iflag = termiosp->c_iflag; + dev->tc_oflag = termiosp->c_oflag; + dev->tc_lflag = termiosp->c_lflag; + } + break; + } + } +#endif + + return ret; } /**************************************************************************** @@ -1010,6 +1194,22 @@ static int uart_open(FAR struct file *filep) dev->recv.head = 0; dev->recv.tail = 0; + /* Initialise termios state */ + +#ifdef CONFIG_SERIAL_TERMIOS + dev->tc_iflag = 0; + if (dev->isconsole == true) + { + /* Enable \n -> \r\n translation for the console */ + + dev->tc_oflag = OPOST | ONLCR; + } + else + { + dev->tc_oflag = 0; + } +#endif + /* Enable the RX interrupt */ uart_enablerxint(dev); @@ -1056,7 +1256,7 @@ int uart_register(FAR const char *path, FAR uart_dev_t *dev) * Name: uart_datareceived * * Description: - * This function is called from uart_recvchars when new serial data is place in + * This function is called from uart_recvchars when new serial data is place in * the driver's circular buffer. This function will wake-up any stalled read() * operations that are waiting for incoming data. * diff --git a/nuttx/drivers/usbdev/cdcacm.c b/nuttx/drivers/usbdev/cdcacm.c index 289e1cd10..441ac907e 100644 --- a/nuttx/drivers/usbdev/cdcacm.c +++ b/nuttx/drivers/usbdev/cdcacm.c @@ -941,7 +941,7 @@ static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver, priv->usbdev = dev; /* Save the reference to our private data structure in EP0 so that it - * can be recovered in ep0 completion events (Unless we are part of + * can be recovered in ep0 completion events (Unless we are part of * a composite device and, in that case, the composite device owns * EP0). */ @@ -1804,9 +1804,10 @@ static void cdcuart_detach(FAR struct uart_dev_s *dev) static int cdcuart_ioctl(FAR struct file *filep,int cmd,unsigned long arg) { - struct inode *inode = filep->f_inode; - struct cdcacm_dev_s *priv = inode->i_private; - int ret = OK; + struct inode *inode = filep->f_inode; + struct cdcacm_dev_s *priv = inode->i_private; + FAR uart_dev_t *serdev = &priv->serdev; + int ret = OK; switch (cmd) { @@ -1898,6 +1899,44 @@ static int cdcuart_ioctl(FAR struct file *filep,int cmd,unsigned long arg) } break; +#ifdef CONFIG_SERIAL_TERMIOS + case TCGETS: + { + struct termios *termiosp = (struct termios*)arg; + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + /* And update with flags from this layer */ + + termiosp->c_iflag = serdev->tc_iflag; + termiosp->c_oflag = serdev->tc_oflag; + termiosp->c_lflag = serdev->tc_lflag; + } + break; + + case TCSETS: + { + struct termios *termiosp = (struct termios*)arg; + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + /* Update the flags we keep at this layer */ + + serdev->tc_iflag = termiosp->c_iflag; + serdev->tc_oflag = termiosp->c_oflag; + serdev->tc_lflag = termiosp->c_lflag; + } + break; +#endif + default: ret = -ENOTTY; break; diff --git a/nuttx/include/nuttx/fs/ioctl.h b/nuttx/include/nuttx/fs/ioctl.h index 8e8647a69..852113a4c 100644 --- a/nuttx/include/nuttx/fs/ioctl.h +++ b/nuttx/include/nuttx/fs/ioctl.h @@ -114,6 +114,13 @@ * is open). */ +#define FIONREAD _FIOC(0x0005) /* IN: Location to return value (int *) + * OUT: Bytes readable from this fd + */ +#define FIONWRITE _FIOC(0x0006) /* IN: Location to return value (int *) + * OUT: Bytes writable to this fd + */ + /* NuttX file system ioctl definitions **************************************/ #define _DIOCVALID(c) (_IOC_TYPE(c)==_DIOCBASE) |