diff --git a/include/tty.h b/include/tty.h index ab43c1414f9..0aebf9d790f 100644 --- a/include/tty.h +++ b/include/tty.h @@ -32,10 +32,12 @@ struct tty_serial { }; /** - * @brief Initialize buffered serial port (classically known as tty). + * @brief Initialize serial port object (classically known as tty). * - * "tty" device provides buffered, interrupt-driver access to an - * underlying UART device. + * "tty" device provides support for buffered, interrupt-driven, + * timeout-controlled access to an underlying UART device. For + * completeness, it also support non-interrupt-driven, busy-polling + * access mode. * * @param tty tty device structure to initialize * @param uart_dev underlying UART device to use (should support @@ -79,6 +81,34 @@ static inline void tty_set_tx_timeout(struct tty_serial *tty, s32_t timeout) tty->tx_timeout = timeout; } +/** + * @brief Set receive buffer for tty device. + * + * Set receive buffer or switch to unbuffered operation for receive. + * + * @param tty tty device structure + * @param buf buffer, or NULL for unbuffered operation + * @param size buffer buffer size, 0 for unbuffered operation + * @return 0 on success, error code (<0) otherwise: + * EINVAL: unsupported buffer (size) + */ +int tty_set_rx_buf(struct tty_serial *tty, void *buf, size_t size); + +/** + * @brief Set transmit buffer for tty device. + * + * Set transmit buffer or switch to unbuffered operation for transmit. + * Note that unbuffered mode is implicitly blocking, i.e. behaves as + * if tty_set_tx_timeout(K_FOREVER) was set. + * + * @param tty tty device structure + * @param buf buffer, or NULL for unbuffered operation + * @param size buffer buffer size, 0 for unbuffered operation + * @return 0 on success, error code (<0) otherwise: + * EINVAL: unsupported buffer (size) + */ +int tty_set_tx_buf(struct tty_serial *tty, void *buf, size_t size); + /** * @brief Read data from a tty device. * diff --git a/subsys/console/tty.c b/subsys/console/tty.c index 257102cf127..39e9f28bd23 100644 --- a/subsys/console/tty.c +++ b/subsys/console/tty.c @@ -104,6 +104,17 @@ ssize_t tty_write(struct tty_serial *tty, const void *buf, size_t size) size_t out_size = 0; int res = 0; + if (tty->tx_ringbuf_sz == 0) { + /* Unbuffered operation, implicitly blocking. */ + out_size = size; + + while (size--) { + uart_poll_out(tty->uart_dev, *p++); + } + + return out_size; + } + while (size--) { res = tty_putchar(tty, *p++); if (res < 0) { @@ -149,12 +160,59 @@ static int tty_getchar(struct tty_serial *tty) return c; } +static ssize_t tty_read_unbuf(struct tty_serial *tty, void *buf, size_t size) +{ + u8_t *p = buf; + size_t out_size = 0; + int res = 0; + u32_t timeout = tty->rx_timeout; + + while (size) { + u8_t c; + res = uart_poll_in(tty->uart_dev, &c); + if (res <= -2) { + /* Error occured, best we can do is to return + * accumulated data w/o error, or return error + * directly if none. + */ + if (out_size == 0) { + errno = res; + return -1; + } + break; + } + + if (res == 0) { + *p++ = c; + out_size++; + size--; + } + + if (size == 0 || (timeout != K_FOREVER && timeout-- == 0)) { + break; + } + + /* Avoid 100% busy-polling, and yet try to process bursts + * of data without extra delays. + */ + if (res == -1) { + k_sleep(1); + } + } + + return out_size; +} + ssize_t tty_read(struct tty_serial *tty, void *buf, size_t size) { u8_t *p = buf; size_t out_size = 0; int res = 0; + if (tty->rx_ringbuf_sz == 0) { + return tty_read_unbuf(tty, buf, size); + } + while (size--) { res = tty_getchar(tty); if (res < 0) { @@ -199,3 +257,31 @@ void tty_init(struct tty_serial *tty, struct device *uart_dev, uart_irq_callback_user_data_set(uart_dev, tty_uart_isr, tty); uart_irq_rx_enable(uart_dev); } + +int tty_set_rx_buf(struct tty_serial *tty, void *buf, size_t size) +{ + uart_irq_rx_disable(tty->uart_dev); + + tty->rx_ringbuf = buf; + tty->rx_ringbuf_sz = size; + + if (size > 0) { + uart_irq_rx_enable(tty->uart_dev); + } + + return 0; +} + +int tty_set_tx_buf(struct tty_serial *tty, void *buf, size_t size) +{ + uart_irq_tx_disable(tty->uart_dev); + + tty->tx_ringbuf = buf; + tty->tx_ringbuf_sz = size; + + /* New buffer is initially empty, no need to re-enable interrupts, + * it will be done when needed (on first output char). + */ + + return 0; +}