diff options
author | px4dev <px4@purgatory.org> | 2012-08-14 09:07:59 -0700 |
---|---|---|
committer | px4dev <px4@purgatory.org> | 2012-08-14 09:07:59 -0700 |
commit | 74980af6c94372e49619b905e9b1b4565930e68a (patch) | |
tree | d902ff890bb38f20c01ecd0cf0428e05d4f3ca2a /nuttx/drivers | |
parent | 34118c72ef88d33d0074914c9bf0cda0232e4940 (diff) | |
parent | 3cc812dbad530e36360a992da9bc4533c016d98d (diff) | |
download | px4-firmware-74980af6c94372e49619b905e9b1b4565930e68a.tar.gz px4-firmware-74980af6c94372e49619b905e9b1b4565930e68a.tar.bz2 px4-firmware-74980af6c94372e49619b905e9b1b4565930e68a.zip |
Merge branch 'NuttX/master' from git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@5027 7fd9a85b-ad96-42d3-883c-3090e2eb8679
Diffstat (limited to 'nuttx/drivers')
-rw-r--r-- | nuttx/drivers/serial/serial.c | 241 |
1 files changed, 194 insertions, 47 deletions
diff --git a/nuttx/drivers/serial/serial.c b/nuttx/drivers/serial/serial.c index ea4ceb0c0..b289bb80b 100644 --- a/nuttx/drivers/serial/serial.c +++ b/nuttx/drivers/serial/serial.c @@ -113,16 +113,29 @@ static const struct file_operations g_serialops = * Name: uart_takesem ************************************************************************************/ -static void uart_takesem(FAR sem_t *sem) +static int uart_takesem(FAR sem_t *sem, bool errout) { - while (sem_wait(sem) != 0) + /* Loop, ignoring interrupts, until we have successfully acquired the semaphore */ + + while (sem_wait(sem) != OK) { - /* The only case that an error should occur here is if - * the wait was awakened by a signal. + /* The only case that an error should occur here is if the wait was awakened + * by a signal. */ ASSERT(get_errno() == EINTR); + + /* When the signal is received, should we errout? Or should we just continue + * waiting until we have the semaphore? + */ + + if (errout) + { + return -EINTR; + } } + + return OK; } /************************************************************************************ @@ -162,10 +175,11 @@ static void uart_pollnotify(FAR uart_dev_t *dev, pollevent_t eventset) * Name: uart_putxmitchar ************************************************************************************/ -static void uart_putxmitchar(FAR uart_dev_t *dev, int ch) +static int uart_putxmitchar(FAR uart_dev_t *dev, int ch) { irqstate_t flags; int nexthead; + int ret; /* 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 @@ -185,36 +199,50 @@ static void uart_putxmitchar(FAR uart_dev_t *dev, int ch) { dev->xmit.buffer[dev->xmit.head] = ch; dev->xmit.head = nexthead; - return; + return OK; } else { - /* Inform the interrupt level logic that we are waiting. - * This and the following steps must be atomic. + /* Inform the interrupt level logic that we are waiting. This and + * the following steps must be atomic. */ flags = irqsave(); dev->xmitwaiting = true; - /* Wait for some characters to be sent from the buffer - * with the TX interrupt enabled. When the TX interrupt - * is enabled, uart_xmitchars should execute and remove - * some of the data from the TX buffer. + /* Wait for some characters to be sent from the buffer with the TX + * interrupt enabled. When the TX interrupt is enabled, uart_xmitchars + * should execute and remove some of the data from the TX buffer. */ uart_enabletxint(dev); - uart_takesem(&dev->xmitsem); + ret = uart_takesem(&dev->xmitsem, true); uart_disabletxint(dev); irqrestore(flags); + + /* Check if we were awakened by signal. */ + + if (ret < 0) + { + /* A signal received while waiting for the xmit buffer to become + * non-full will abort the transfer. + */ + + return -EINTR; + } } } + + /* We won't get here */ + + return OK; } /************************************************************************************ * Name: uart_irqwrite ************************************************************************************/ -static ssize_t uart_irqwrite(FAR uart_dev_t *dev, FAR const char *buffer, size_t buflen) +static inline ssize_t uart_irqwrite(FAR uart_dev_t *dev, FAR const char *buffer, size_t buflen) { ssize_t ret = buflen; @@ -223,14 +251,17 @@ static ssize_t uart_irqwrite(FAR uart_dev_t *dev, FAR const char *buffer, size_t for (; buflen; buflen--) { int ch = *buffer++; - uart_putc(ch); - /* If this is the console, then we should replace LF with LF-CR */ + /* If this is the console, then we should replace LF with CR-LF */ if (ch == '\n') { uart_putc('\r'); } + + /* Output the character, using the low-level direct UART interfaces */ + + uart_putc(ch); } return ret; @@ -242,18 +273,22 @@ static ssize_t uart_irqwrite(FAR uart_dev_t *dev, FAR const char *buffer, size_t 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 ret = buflen; - - /* 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. + FAR struct inode *inode = filep->f_inode; + FAR uart_dev_t *dev = inode->i_private; + ssize_t nread = buflen; + int ret; + + /* 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. */ if (up_interrupt_context() || getpid() == 0) { + /* up_putc() will be used to generate the output in a busy-wait loop. + * up_putc() is only available for the console device. + */ + if (dev->isconsole) { irqstate_t flags = irqsave(); @@ -263,13 +298,22 @@ static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, size_t } else { - return ERROR; + return -EPERM; } } - /* Only one user can be accessing dev->xmit.head at once */ + /* Only one user can access dev->xmit.head at a time */ - uart_takesem(&dev->xmit.sem); + ret = (ssize_t)uart_takesem(&dev->xmit.sem, true); + if (ret < 0) + { + /* A signal received while waiting for access to the xmit.head will + * abort the transfer. After the transfer has started, we are committed + * and signals will be ignored. + */ + + return ret; + } /* Loop while we still have data to copy to the transmit buffer. * we add data to the head of the buffer; uart_xmitchars takes the @@ -281,15 +325,50 @@ static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, size_t { int ch = *buffer++; + /* If the ONLCR flag is set, we should translate \n to \r\n */ + + ret = OK; + if ((ch == '\n') && (dev->termios_s.c_oflag && ONLCR)) + { + ret = uart_putxmitchar(dev, '\r'); + } + /* Put the character into the transmit buffer */ - uart_putxmitchar(dev, ch); + if (ret == OK) + { + ret = uart_putxmitchar(dev, ch); + } - /* If this is the console, then we should replace LF with LF-CR */ + /* Were we awakened by a signal? That should be the only condition that + * uart_putxmitchar() should return an error. + */ - if ((dev->termios_s.c_oflag && ONLCR) && ch == '\n') + if (ret < 0) { - uart_putxmitchar(dev, '\r'); + /* 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. + */ + + if (buflen < nread) + { + /* Some data was transferred. Return the number of bytes that were + * successfully transferred. + */ + + nread -= buflen; + } + else + { + /* No data was transferred. Return -EINTR. The VFS layer will + * set the errno value appropriately). + */ + + nread = -EINTR; + } + + break; } } @@ -299,7 +378,7 @@ static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, size_t } uart_givesem(&dev->xmit.sem); - return ret; + return nread; } /************************************************************************************ @@ -313,10 +392,20 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen irqstate_t flags; ssize_t recvd = 0; int16_t tail; + int ret; + + /* Only one user can access dev->recv.tail at a time */ - /* Only one user can be accessing dev->recv.tail at once */ + ret = uart_takesem(&dev->recv.sem, true); + if (ret < 0) + { + /* A signal received while waiting for access to the recv.tail will avort + * the transfer. After the transfer has started, we are committed and + * signals will be ignored. + */ - uart_takesem(&dev->recv.sem); + return ret; + } /* Loop while we still have data to copy to the receive buffer. * we add data to the head of the buffer; uart_xmitchars takes the @@ -353,6 +442,7 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen { tail = 0; } + dev->recv.tail = tail; } @@ -427,12 +517,34 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen uart_enablerxint(dev); /* Now wait with the Rx interrupt re-enabled. NuttX will - * automatically re-enable global interrupts when this - * thread goes to sleep. + * automatically re-enable global interrupts when this thread + * goes to sleep. */ - uart_takesem(&dev->recvsem); + ret = uart_takesem(&dev->recvsem, true); irqrestore(flags); + + /* Was a signal received while waiting for data to be received? */ + + if (ret < 0) + { + /* POSIX requires that we return after a signal is received. + * If some bytes were read, we need to return the number of bytes + * read; if no bytes were read, we need to return -1 with the + * errno set correctly. + */ + + if (recvd == 0) + { + /* No bytes were read, return -EINTR (the VFS layer will + * set the errno value appropriately. + */ + + recvd = -EINTR; + } + + break; + } } else { @@ -513,7 +625,7 @@ int uart_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) FAR uart_dev_t *dev = inode->i_private; pollevent_t eventset; int ndx; - int ret = OK; + int ret; int i; /* Some sanity checking */ @@ -527,7 +639,16 @@ int uart_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) /* Are we setting up the poll? Or tearing it down? */ - uart_takesem(&dev->pollsem); + ret = uart_takesem(&dev->pollsem, true); + if (ret < 0) + { + /* A signal received while waiting for access to the poll data + * will abort the operation. + */ + + return ret; + } + if (setup) { /* This is a request to set up the poll. Find an available @@ -555,31 +676,43 @@ int uart_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) goto errout; } - /* Should immediately notify on any of the requested events? + /* Should we immediately notify on any of the requested events? * First, check if the xmit buffer is full. + * + * Get exclusive access to the xmit buffer indices. NOTE: that we do not + * let this wait be interrupted by a signal (we probably should, but that + * would be a little awkward). */ eventset = 0; + (void)uart_takesem(&dev->xmit.sem, false); - uart_takesem(&dev->xmit.sem); ndx = dev->xmit.head + 1; if (ndx >= dev->xmit.size) { ndx = 0; } + if (ndx != dev->xmit.tail) { eventset |= 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 + * would be a little awkward). + */ - uart_takesem(&dev->recv.sem); + (void)uart_takesem(&dev->recv.sem, false); if (dev->recv.head != dev->recv.tail) { eventset |= POLLIN; } + uart_givesem(&dev->recv.sem); if (eventset) @@ -629,7 +762,13 @@ static int uart_close(FAR struct file *filep) FAR uart_dev_t *dev = inode->i_private; irqstate_t flags; - uart_takesem(&dev->closesem); + /* Get exclusive access to the close semaphore (to synchronize open/close operations. + * NOTE: that we do not let this wait be interrupted by a signal. Technically, we + * should, but almost no one every checks the return value from close() so we avoid + * a potential memory leak by ignoring signals in this case. + */ + + (void)uart_takesem(&dev->closesem, false); if (dev->open_count > 1) { dev->open_count--; @@ -694,11 +833,19 @@ static int uart_open(FAR struct file *filep) struct inode *inode = filep->f_inode; uart_dev_t *dev = inode->i_private; uint8_t tmp; - int ret = OK; + int ret; - /* If the port is the middle of closing, wait until the close is finished */ + /* 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. + */ + + ret = uart_takesem(&dev->closesem, true); + if (ret < 0) + { + /* A signal received while waiting for the last close operation. */ - uart_takesem(&dev->closesem); + return ret; + } /* Start up serial port */ /* Increment the count of references to the device. */ |