From b319c27f03e3a9cd92ed7febf077ba6b210ca7d2 Mon Sep 17 00:00:00 2001 From: yangsong8 Date: Fri, 9 Aug 2024 19:06:05 +0800 Subject: [PATCH] serial: Added APIs for receiving and sending multiple chars Signed-off-by: yangsong8 --- drivers/serial/serial.c | 61 ++++++++++----- drivers/serial/serial_io.c | 142 +++++++++++++++++++++++----------- include/nuttx/serial/serial.h | 16 ++++ 3 files changed, 152 insertions(+), 67 deletions(-) diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index c8a15797f1c..5c20fea922e 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -294,13 +294,32 @@ static int uart_putxmitchar(FAR uart_dev_t *dev, int ch, bool oktoblock) * Name: uart_putc ****************************************************************************/ -static inline void uart_putc(FAR uart_dev_t *dev, int ch) +static inline void uart_putchars(FAR uart_dev_t *dev, + FAR const void *buf, size_t len) { - while (!uart_txready(dev)) - { - } + FAR const char *pbuf = buf; - uart_send(dev, ch); + while (len > 0) + { + while (!uart_txready(dev)) + { + } + + if (dev->ops->sendbuf) + { + ssize_t ret = uart_sendbuf(dev, pbuf, len); + if (ret > 0) + { + pbuf += ret; + len -= ret; + } + } + else + { + uart_send(dev, *pbuf++); + len--; + } + } } /**************************************************************************** @@ -311,39 +330,41 @@ static inline ssize_t uart_irqwrite(FAR uart_dev_t *dev, FAR const char *buffer, size_t buflen) { - ssize_t ret = buflen; + size_t tail = 0; + size_t head = buflen; - /* Force each character through the low level interface */ + /* Do output post-processing */ - for (; buflen; buflen--) + if ((dev->tc_oflag & OPOST) != 0) { - int ch = *buffer++; - - /* Do output post-processing */ - - if ((dev->tc_oflag & OPOST) != 0) + for (head = 0; head < buflen; head++) { + int ch = buffer[head]; + /* Mapping CR to NL? */ if ((ch == '\r') && (dev->tc_oflag & OCRNL) != 0) { - ch = '\n'; + uart_putchars(dev, &buffer[tail], head - tail); + uart_putchars(dev, "\n", 1); + tail = head + 1; } /* Are we interested in newline processing? */ if ((ch == '\n') && (dev->tc_oflag & (ONLCR | ONLRET)) != 0) { - uart_putc(dev, '\r'); + uart_putchars(dev, &buffer[tail], head - tail); + uart_putchars(dev, "\r", 1); + tail = head; } } - - /* Output the character, using the low-level direct UART interfaces */ - - uart_putc(dev, ch); } - return ret; + /* Output the character, using the low-level direct UART interfaces */ + + uart_putchars(dev, &buffer[tail], head - tail); + return buflen; } /**************************************************************************** diff --git a/drivers/serial/serial_io.c b/drivers/serial/serial_io.c index e3ea1861446..45ddcf36fd3 100644 --- a/drivers/serial/serial_io.c +++ b/drivers/serial/serial_io.c @@ -66,12 +66,37 @@ void uart_xmitchars(FAR uart_dev_t *dev) { /* Send the next byte */ - uart_send(dev, dev->xmit.buffer[dev->xmit.tail]); - nbytes++; + if (dev->ops->sendbuf) + { + ssize_t sent; + + if (dev->xmit.tail < dev->xmit.head) + { + sent = dev->xmit.head - dev->xmit.tail; + } + else + { + sent = dev->xmit.size - dev->xmit.tail; + } + + sent = uart_sendbuf(dev, + &dev->xmit.buffer[dev->xmit.tail], + sent); + if (sent > 0) + { + dev->xmit.tail += sent; + nbytes += sent; + } + } + else + { + uart_send(dev, dev->xmit.buffer[dev->xmit.tail++]); + nbytes++; + } /* Increment the tail index */ - if (++(dev->xmit.tail) >= dev->xmit.size) + if (dev->xmit.tail >= dev->xmit.size) { dev->xmit.tail = 0; } @@ -120,38 +145,28 @@ void uart_recvchars(FAR uart_dev_t *dev) { FAR struct uart_buffer_s *rxbuf = &dev->recv; #ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS - unsigned int watermark; + /* Pre-calculate the watermark level that we will need to test against. */ + + unsigned int watermark = + (CONFIG_SERIAL_IFLOWCONTROL_UPPER_WATERMARK * rxbuf->size) / 100; #endif - unsigned int status; - int nexthead = rxbuf->head + 1; #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) int signo = 0; #endif uint16_t nbytes = 0; - if (nexthead >= rxbuf->size) - { - nexthead = 0; - } - -#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS - /* Pre-calculate the watermark level that we will need to test against. */ - - watermark = (CONFIG_SERIAL_IFLOWCONTROL_UPPER_WATERMARK * rxbuf->size) / - 100; -#endif - /* Loop putting characters into the receive buffer until there are no * further characters to available. */ while (uart_rxavailable(dev)) { + int nexthead = rxbuf->head + 1 < rxbuf->size ? rxbuf->head + 1 : 0; bool is_full = (nexthead == rxbuf->tail); + FAR char *pbuf; char ch; -#ifdef CONFIG_SERIAL_IFLOWCONTROL #ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS unsigned int nbuffered; @@ -181,7 +196,7 @@ void uart_recvchars(FAR uart_dev_t *dev) break; } } -#else +#elif defined(CONFIG_SERIAL_IFLOWCONTROL) /* Check if RX buffer is full and allow serial low-level driver to * pause processing. This allows proper utilization of hardware flow * control. @@ -196,41 +211,74 @@ void uart_recvchars(FAR uart_dev_t *dev) break; } } -#endif #endif /* Get this next character from the hardware */ - ch = uart_receive(dev, &status); + if (!is_full && dev->ops->recvbuf) + { + ssize_t ret; + + if (rxbuf->tail > rxbuf->head) + { + nbytes = rxbuf->tail - rxbuf->head - 1; + } + else if (rxbuf->tail) + { + nbytes = rxbuf->size - rxbuf->head; + } + else + { + nbytes = rxbuf->size - rxbuf->head - 1; + } + + pbuf = &rxbuf->buffer[rxbuf->head]; + ret = uart_recvbuf(dev, pbuf, nbytes); + if (ret <= 0) + { + continue; + } + + nbytes = ret; + rxbuf->head += nbytes; + if (rxbuf->head >= rxbuf->size) + { + rxbuf->head = 0; + } + } + else + { + unsigned int status; + + ch = uart_receive(dev, &status); + pbuf = &ch; + nbytes = 1; + + /* If the RX buffer becomes full, then the serial data is + * discarded. This is necessary because on most serial hardware, + * you must read the data in order to clear the RX interrupt. + * An option on some hardware might be to simply disable RX + * interrupts until the RX buffer becomes non-FULL. However, that + * would probably just cause the overrun to occur in hardware + * (unless it has some large internal buffering). + */ + + if (!is_full) + { + /* Add the character to the buffer */ + + rxbuf->buffer[rxbuf->head] = ch; + + /* Increment the head index */ + + rxbuf->head = nexthead; + } + } #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) - signo = uart_check_special(dev, &ch, 1); + signo = uart_check_special(dev, pbuf, nbytes); #endif - - /* If the RX buffer becomes full, then the serial data is discarded. - * This is necessary because on most serial hardware, you must read - * the data in order to clear the RX interrupt. An option on some - * hardware might be to simply disable RX interrupts until the RX - * buffer becomes non-FULL. However, that would probably just cause - * the overrun to occur in hardware (unless it has some large internal - * buffering). - */ - - if (!is_full) - { - /* Add the character to the buffer */ - - rxbuf->buffer[rxbuf->head] = ch; - - /* Increment the head index */ - - rxbuf->head = nexthead; - if (++nexthead >= rxbuf->size) - { - nexthead = 0; - } - } } /* If any bytes were added to the buffer, inform any waiters there is new diff --git a/include/nuttx/serial/serial.h b/include/nuttx/serial/serial.h index 772fd23566a..7b6e1201dae 100644 --- a/include/nuttx/serial/serial.h +++ b/include/nuttx/serial/serial.h @@ -87,6 +87,8 @@ #define uart_txempty(dev) dev->ops->txempty(dev) #define uart_send(dev,ch) dev->ops->send(dev,ch) #define uart_receive(dev,s) dev->ops->receive(dev,s) +#define uart_recvbuf(dev,b,l) dev->ops->recvbuf(dev,b,l) +#define uart_sendbuf(dev,b,l) dev->ops->sendbuf(dev,b,l) #define uart_release(dev) \ ((dev)->ops->release ? (dev)->ops->release(dev) : -ENOSYS) @@ -263,6 +265,20 @@ struct uart_ops_s */ CODE int (*release)(FAR struct uart_dev_s *dev); + + /* Receive multiple bytes. + * Returns the actual number of characters received. + */ + + CODE ssize_t (*recvbuf)(FAR struct uart_dev_s *dev, + FAR void *buf, size_t len); + + /* This method will send multiple bytes. + * Returns the actual number of characters sent. + */ + + CODE ssize_t (*sendbuf)(FAR struct uart_dev_s *dev, + FAR const void *buf, size_t len); }; /* This is the device structure used by the driver. The caller of