diff options
author | Lorenz Meier <lm@inf.ethz.ch> | 2013-07-18 12:44:39 +0200 |
---|---|---|
committer | Lorenz Meier <lm@inf.ethz.ch> | 2013-07-18 12:44:39 +0200 |
commit | dcfc43c3bbe754884f1256551c057536b864ca1a (patch) | |
tree | a8b3eb99867c5550ad6be9260b217e6debed1e0c /nuttx/drivers | |
parent | f9613983fd455f6c8b0fe28d07a68e0e5ea498d3 (diff) | |
download | px4-nuttx-dcfc43c3bbe754884f1256551c057536b864ca1a.tar.gz px4-nuttx-dcfc43c3bbe754884f1256551c057536b864ca1a.tar.bz2 px4-nuttx-dcfc43c3bbe754884f1256551c057536b864ca1a.zip |
Added support for FIONREAD and FIONWRITE, added input / output processing support for UART and CDCACM serial ports. Implemented by Mike Smith, Andrew Tridgell and Lorenz Meier
Diffstat (limited to 'nuttx/drivers')
-rw-r--r-- | nuttx/drivers/serial/serial.c | 266 | ||||
-rw-r--r-- | nuttx/drivers/usbdev/cdcacm.c | 44 |
2 files changed, 266 insertions, 44 deletions
diff --git a/nuttx/drivers/serial/serial.c b/nuttx/drivers/serial/serial.c index 912feb255..cbe502cc9 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 @@ -324,6 +325,7 @@ static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, size_t 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,59 +403,64 @@ 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++; - /* If this is the console, then we should replace LF with CR-LF */ + /* Do output post-processing */ - ret = OK; - if (dev->isconsole && ch == '\n') - { - ret = uart_putxmitchar(dev, '\r', oktoblock); - } +#ifdef CONFIG_SERIAL_TERMIOS - /* Put the character into the transmit buffer */ + if (dev->tc_oflag & OPOST) + { - if (ret == OK) - { - ret = uart_putxmitchar(dev, ch, oktoblock); - } + /* Mapping CR to NL? */ - /* uart_putxmitchar() might return an error under one of two - * conditions: (1) The wait for buffer space might have been - * interrupted by a signal (ret should be -EINTR), (2) if - * CONFIG_SERIAL_REMOVABLE is defined, then uart_putxmitchar() - * might also return if the serial device was disconnected - * (with -ENOTCONN), or (3) if O_NONBLOCK is specified, then - * then uart_putxmitchar() might return -EAGAIN if the output - * TX buffer is full. - */ + if ((ch == '\r') && (dev->tc_oflag & OCRNL)) + { + ch = '\n'; + } - if (ret < 0) - { - /* POSIX requires that we return -1 and errno set if no data was - * transferred. Otherwise, we return the number of bytes in the - * interrupted transfer. - */ + /* Are we interested in newline processing? */ - if (buflen < nread) + if ((ch == '\n') && (dev->tc_oflag & (ONLCR | ONLRET))) { - /* Some data was transferred. Return the number of bytes that - * were successfully transferred. - */ + ret = uart_putxmitchar(dev, '\r', oktoblock); - nread -= buflen; - } - else - { - /* No data was transferred. Return the negated errno value. - * The VFS layer will set the errno value appropriately). - */ - - nread = ret; + if (ret != OK) + { + 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 */ + + if (dev->isconsole && ch == '\n') + { + ret = uart_putxmitchar(dev, '\r', oktoblock); + } + +#endif + + /* Put the character into the transmit buffer */ + + ret = uart_putxmitchar(dev, ch, oktoblock); + + if (ret != OK) + { break; } + } if (dev->xmit.head != dev->xmit.tail) @@ -477,6 +484,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 +538,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 +551,49 @@ 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 +748,118 @@ 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; + } + } + } + + /* Append any higher level TTY flags */ + + else if (ret == OK) + { + switch (cmd) + { +#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 = 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 +1171,25 @@ 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); diff --git a/nuttx/drivers/usbdev/cdcacm.c b/nuttx/drivers/usbdev/cdcacm.c index 289e1cd10..ec3dcd333 100644 --- a/nuttx/drivers/usbdev/cdcacm.c +++ b/nuttx/drivers/usbdev/cdcacm.c @@ -1806,6 +1806,7 @@ 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; + FAR uart_dev_t *serdev = &priv->serdev; int ret = OK; switch (cmd) @@ -1898,8 +1899,49 @@ 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; + + ret = -ENOSYS; break; } |