summaryrefslogtreecommitdiff
path: root/nuttx/drivers/serial
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-08-10 19:14:05 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-08-10 19:14:05 -0600
commit3c5fcd7ca22c13071fc85c89036adf43f92ebaf2 (patch)
tree622e43facf3bd417459d2681c47bab360d4a33e1 /nuttx/drivers/serial
parent9a16be559048f93c3396183b1dc1261781d54b01 (diff)
downloadnuttx-3c5fcd7ca22c13071fc85c89036adf43f92ebaf2.tar.gz
nuttx-3c5fcd7ca22c13071fc85c89036adf43f92ebaf2.tar.bz2
nuttx-3c5fcd7ca22c13071fc85c89036adf43f92ebaf2.zip
Serial FIONREAD, FIONWRITE, and TERMIOS I/O processing from Mike Smith, Andrew Tridgell, and and Lorenz Meier
Diffstat (limited to 'nuttx/drivers/serial')
-rw-r--r--nuttx/drivers/serial/serial.c220
1 files changed, 210 insertions, 10 deletions
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.
*