Serial FIONREAD, FIONWRITE, and TERMIOS I/O processing from Mike Smith, Andrew Tridgell, and and Lorenz Meier

This commit is contained in:
Gregory Nutt
2013-08-10 19:14:05 -06:00
parent 97e13b33ab
commit 82b528e0c8
5 changed files with 269 additions and 17 deletions
+5 -1
View File
@@ -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).
+3 -1
View File
@@ -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 */
+211 -11
View File
@@ -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++;
/* If this is the console, then we should replace LF with CR-LF */
ch = *buffer++;
ret = OK;
#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 */
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.
*
+43 -4
View File
@@ -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;
+7
View File
@@ -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)