Fix readline return value; Add support for removable serial devices

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5589 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo
2013-01-31 16:52:20 +00:00
parent d512869b62
commit cdbbfaa464
8 changed files with 281 additions and 50 deletions
+3
View File
@@ -10,6 +10,9 @@ config DEV_LOWCONSOLE
---help---
Use the simple, low-level, write-only serial console driver (minimal support)
config SERIAL_REMOVABLE
bool
config 16550_UART
bool "16550 UART Chip support"
default n
+131 -6
View File
@@ -1,7 +1,7 @@
/************************************************************************************
* drivers/serial/serial.c
*
* Copyright (C) 2007-2009, 2011-2012 Gregory Nutt. All rights reserved.
* Copyright (C) 2007-2009, 2011-2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -157,7 +157,11 @@ static void uart_pollnotify(FAR uart_dev_t *dev, pollevent_t eventset)
struct pollfd *fds = dev->fds[i];
if (fds)
{
#ifdef CONFIG_SERIAL_REMOVABLE
fds->revents |= ((fds->events | (POLLERR|POLLHUP)) & eventset);
#else
fds->revents |= (fds->events & eventset);
#endif
if (fds->revents != 0)
{
fvdbg("Report events: %02x\n", fds->revents);
@@ -219,6 +223,15 @@ static int uart_putxmitchar(FAR uart_dev_t *dev, int ch)
uart_disabletxint(dev);
irqrestore(flags);
#ifdef CONFIG_SERIAL_REMOVABLE
/* Check if the removable device is no longer connected */
if (dev->disconnected)
{
return -ENOTCONN;
}
#endif
/* Check if we were awakened by signal. */
if (ret < 0)
@@ -279,6 +292,15 @@ static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, size_t
ssize_t nread = buflen;
int ret;
#ifdef CONFIG_SERIAL_REMOVABLE
/* If the removable device is no longer connected, refuse to write to the device */
if (dev->disconnected)
{
return -ENOTCONN;
}
#endif
/* 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
* a little differently.
@@ -395,6 +417,15 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen
int16_t tail;
int ret;
#ifdef CONFIG_SERIAL_REMOVABLE
/* If the removable device is no longer connected, refuse to read from the device */
if (dev->disconnected)
{
return -ENOTCONN;
}
#endif
/* Only one user can access dev->recv.tail at a time */
ret = uart_takesem(&dev->recv.sem, true);
@@ -525,9 +556,15 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen
ret = uart_takesem(&dev->recvsem, true);
irqrestore(flags);
/* Was a signal received while waiting for data to be received? */
/* Was a signal received while waiting for data to be
* received? Was a removable device disconnected?
*/
#ifdef CONFIG_SERIAL_REMOVABLE
if (ret < 0 || dev->disconnected)
#else
if (ret < 0)
#endif
{
/* POSIX requires that we return after a signal is received.
* If some bytes were read, we need to return the number of bytes
@@ -541,7 +578,11 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen
* set the errno value appropriately.
*/
#ifdef CONFIG_SERIAL_REMOVABLE
recvd = dev->disconnected ? -ENOTCONN : -EINTR;
#else
recvd = -EINTR;
#endif
}
break;
@@ -656,12 +697,12 @@ int uart_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
if (ndx != dev->xmit.tail)
{
eventset |= POLLOUT;
eventset |= (fds->events & POLLOUT);
}
uart_givesem(&dev->xmit.sem);
/* Check if the receive buffer is empty
/* Check if the receive buffer is empty.
*
* Get exclusive access to the recv buffer indices. NOTE: that we do not
* let this wait be interrupted by a signal (we probably should, but that
@@ -671,11 +712,20 @@ int uart_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
(void)uart_takesem(&dev->recv.sem, false);
if (dev->recv.head != dev->recv.tail)
{
eventset |= POLLIN;
eventset |= (fds->events & POLLIN);
}
uart_givesem(&dev->recv.sem);
#ifdef CONFIG_SERIAL_REMOVABLE
/* Check if a removable device has been disconnected. */
if (dev->disconnected)
{
eventset |= (POLLERR|POLLHUP);
}
#endif
if (eventset)
{
uart_pollnotify(dev, eventset);
@@ -796,6 +846,15 @@ static int uart_open(FAR struct file *filep)
uint8_t tmp;
int ret;
#ifdef CONFIG_SERIAL_REMOVABLE
/* If the removable device is no longer connected, refuse to open the device */
if (dev->disconnected)
{
return -ENOTCONN;
}
#endif
/* If the port is the middle of closing, wait until the close is finished.
* If a signal is received while we are waiting, then return EINTR.
*/
@@ -915,10 +974,12 @@ int uart_register(FAR const char *path, FAR uart_dev_t *dev)
void uart_datareceived(FAR uart_dev_t *dev)
{
/* Awaken any awaiting read() operations */
/* Is there a thread waiting for read data? */
if (dev->recvwaiting)
{
/* Yes... wake it up */
dev->recvwaiting = false;
(void)sem_post(&dev->recvsem);
}
@@ -942,8 +1003,12 @@ void uart_datareceived(FAR uart_dev_t *dev)
void uart_datasent(FAR uart_dev_t *dev)
{
/* Is there a thread waiting for space in xmit.buffer? */
if (dev->xmitwaiting)
{
/* Yes... wake it up */
dev->xmitwaiting = false;
(void)sem_post(&dev->xmitsem);
}
@@ -953,4 +1018,64 @@ void uart_datasent(FAR uart_dev_t *dev)
uart_pollnotify(dev, POLLOUT);
}
/************************************************************************************
* Name: uart_connected
*
* Description:
* Serial devices (like USB serial) can be removed. In that case, the "upper
* half" serial driver must be informed that there is no longer a valid serial
* channel associated with the driver.
*
* In this case, the driver will terminate all pending transfers wint ENOTCONN and
* will refuse all further transactions while the "lower half" is disconnected.
* The driver will continue to be registered, but will be in an unusable state.
*
* Conversely, the "upper half" serial driver needs to know when the serial
* device is reconnected so that it can resume normal operations.
*
* Assumptions/Limitations:
* This function may be called from an interrupt handler.
*
************************************************************************************/
#ifdef CONFIG_SERIAL_REMOVABLE
void uart_connected(FAR uart_dev_t *dev, bool connected)
{
/* Is the device disconnected? */
dev->disconnected = !connected;
if (!connected)
{
/* Yes.. wake up all waiting threads. Each thread should detect the
* disconnection and return the ENOTCONN error.
*/
/* Is there a thread waiting for space in xmit.buffer? */
if (dev->xmitwaiting)
{
/* Yes... wake it up */
dev->xmitwaiting = false;
(void)sem_post(&dev->xmitsem);
}
/* Is there a thread waiting for read data? */
if (dev->recvwaiting)
{
/* Yes... wake it up */
dev->recvwaiting = false;
(void)sem_post(&dev->recvsem);
}
/* Notify all poll/select waiters that and hangup/error occurred */
uart_pollnotify(dev, (POLLERR|POLLHUP));
}
}
#endif
+2
View File
@@ -150,6 +150,7 @@ endif
menuconfig PL2303
bool "Prolific PL2303 serial/USB converter emulation"
default n
select SERIAL_REMOVABLE
---help---
This logic emulates the Prolific PL2303 serial/USB converter
@@ -222,6 +223,7 @@ endif
menuconfig CDCACM
bool "USB Modem (CDC ACM) support"
default n
select SERIAL_REMOVABLE
---help---
Enables USB Modem (CDC ACM) support
+39 -8
View File
@@ -570,6 +570,14 @@ static void cdcacm_resetconfig(FAR struct cdcacm_dev_s *priv)
priv->config = CDCACM_CONFIGIDNONE;
/* Inform the "upper half" driver that there is no (functional) USB
* connection.
*/
#ifdef CONFIG_SERIAL_REMOVABLE
uart_connected(&priv->serdev, false);
#endif
/* Disable endpoints. This should force completion of all pending
* transfers.
*/
@@ -731,10 +739,20 @@ static int cdcacm_setconfig(FAR struct cdcacm_dev_s *priv, uint8_t config)
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-ret);
goto errout;
}
priv->nrdq++;
}
/* We are successfully configured */
priv->config = config;
/* Inform the "upper half" driver that we are "open for business" */
#ifdef CONFIG_SERIAL_REMOVABLE
uart_connected(&priv->serdev, true);
#endif
return OK;
errout:
@@ -1575,12 +1593,20 @@ static void cdcacm_disconnect(FAR struct usbdevclass_driver_s *driver,
}
#endif
/* Reset the configuration */
/* Inform the "upper half serial driver that we have lost the USB serial
* connection.
*/
flags = irqsave();
#ifdef CONFIG_SERIAL_REMOVABLE
uart_connected(&priv->serdev, false);
#endif
/* Reset the configuration */
cdcacm_resetconfig(priv);
/* Clear out all data in the circular buffer */
/* Clear out all outgoing data in the circular buffer */
priv->serdev.xmit.head = 0;
priv->serdev.xmit.tail = 0;
@@ -2045,12 +2071,17 @@ int cdcacm_classobject(int minor, FAR struct usbdevclass_driver_s **classdev)
/* Initialize the serial driver sub-structure */
priv->serdev.recv.size = CONFIG_CDCACM_RXBUFSIZE;
priv->serdev.recv.buffer = priv->rxbuffer;
priv->serdev.xmit.size = CONFIG_CDCACM_TXBUFSIZE;
priv->serdev.xmit.buffer = priv->txbuffer;
priv->serdev.ops = &g_uartops;
priv->serdev.priv = priv;
/* The initial state is disconnected */
#ifdef CONFIG_SERIAL_REMOVABLE
priv->serdev.disconnected = true;
#endif
priv->serdev.recv.size = CONFIG_CDCACM_RXBUFSIZE;
priv->serdev.recv.buffer = priv->rxbuffer;
priv->serdev.xmit.size = CONFIG_CDCACM_TXBUFSIZE;
priv->serdev.xmit.buffer = priv->txbuffer;
priv->serdev.ops = &g_uartops;
priv->serdev.priv = priv;
/* Initialize the USB class driver structure */
+37 -8
View File
@@ -983,6 +983,14 @@ static void usbclass_resetconfig(FAR struct pl2303_dev_s *priv)
priv->config = PL2303_CONFIGIDNONE;
/* Inform the "upper half" driver that there is no (functional) USB
* connection.
*/
#ifdef CONFIG_SERIAL_REMOVABLE
uart_connected(&priv->serdev, false);
#endif
/* Disable endpoints. This should force completion of all pending
* transfers.
*/
@@ -1112,10 +1120,20 @@ static int usbclass_setconfig(FAR struct pl2303_dev_s *priv, uint8_t config)
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-ret);
goto errout;
}
priv->nrdq++;
}
/* We are successfully configured */
priv->config = config;
/* Inform the "upper half" driver that we are "open for business" */
#ifdef CONFIG_SERIAL_REMOVABLE
uart_connected(&priv->serdev, true);
#endif
return OK;
errout:
@@ -1844,12 +1862,20 @@ static void usbclass_disconnect(FAR struct usbdevclass_driver_s *driver,
}
#endif
/* Reset the configuration */
/* Inform the "upper half serial driver that we have lost the USB serial
* connection.
*/
flags = irqsave();
#ifdef CONFIG_SERIAL_REMOVABLE
uart_connected(&priv->serdev, false);
#endif
/* Reset the configuration */
usbclass_resetconfig(priv);
/* Clear out all data in the circular buffer */
/* Clear out all outgoing data in the circular buffer */
priv->serdev.xmit.head = 0;
priv->serdev.xmit.tail = 0;
@@ -2185,12 +2211,15 @@ int usbdev_serialinitialize(int minor)
/* Initialize the serial driver sub-structure */
priv->serdev.recv.size = CONFIG_PL2303_RXBUFSIZE;
priv->serdev.recv.buffer = priv->rxbuffer;
priv->serdev.xmit.size = CONFIG_PL2303_TXBUFSIZE;
priv->serdev.xmit.buffer = priv->txbuffer;
priv->serdev.ops = &g_uartops;
priv->serdev.priv = priv;
#ifdef CONFIG_SERIAL_REMOVABLE
priv->serdev.disconnected = true;
#endif
priv->serdev.recv.size = CONFIG_PL2303_RXBUFSIZE;
priv->serdev.recv.buffer = priv->rxbuffer;
priv->serdev.xmit.size = CONFIG_PL2303_TXBUFSIZE;
priv->serdev.xmit.buffer = priv->txbuffer;
priv->serdev.ops = &g_uartops;
priv->serdev.priv = priv;
/* Initialize the USB class driver structure */