summaryrefslogtreecommitdiff
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
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
-rw-r--r--nuttx/ChangeLog6
-rw-r--r--nuttx/drivers/mmcsd/mmcsd_sdio.c4
-rw-r--r--nuttx/drivers/serial/serial.c220
-rw-r--r--nuttx/drivers/usbdev/cdcacm.c47
-rw-r--r--nuttx/include/nuttx/fs/ioctl.h7
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)