From 9316d919a79506f43f1bf934f386c7583f023326 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 22 Oct 2018 21:01:22 +0300 Subject: [PATCH 1/3] subsys: console: tty: Allow to specify receive timeout This allows to specify receive timeout, instead of previously hardcoded K_FOREVER value. K_FOREVER is still the default, and can be changes after tty initialization using tty_set_rx_timeout() call, and timeout is stored as a property of tty. (Instead of e.g. being a param of each receive call. Handling like that is required for POSIX-like behavior of tty). Signed-off-by: Paul Sokolovsky --- include/console.h | 18 +++++++++++++++++- subsys/console/getchar.c | 3 ++- subsys/console/tty.c | 10 ++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/include/console.h b/include/console.h index 437609fe3c713..599036bdc7740 100644 --- a/include/console.h +++ b/include/console.h @@ -21,6 +21,7 @@ struct tty_serial { u8_t *rx_ringbuf; u32_t rx_ringbuf_sz; u16_t rx_get, rx_put; + s32_t rx_timeout; u8_t *tx_ringbuf; u32_t tx_ringbuf_sz; @@ -47,13 +48,28 @@ void tty_init(struct tty_serial *tty, struct device *uart_dev, u8_t *rxbuf, u16_t rxbuf_sz, u8_t *txbuf, u16_t txbuf_sz); +/** + * @brief Set receive timeout for tty device. + * + * Set timeout for getchar() operation. Default timeout after + * device initialization is K_FOREVER. + * + * @param tty tty device structure + * @param timeout timeout in milliseconds, or K_FOREVER, or K_NO_WAIT + */ +static inline void tty_set_rx_timeout(struct tty_serial *tty, s32_t timeout) +{ + tty->rx_timeout = timeout; +} /** * @brief Input a character from a tty device. * * @param tty tty device structure + * @return >=0, an input character + * <0, an error (e.g. -EAGAIN if timeout expired) */ -u8_t tty_getchar(struct tty_serial *tty); +int tty_getchar(struct tty_serial *tty); /** * @brief Output a character from to tty device. diff --git a/subsys/console/getchar.c b/subsys/console/getchar.c index 6b36d12a35eca..6b313563be566 100644 --- a/subsys/console/getchar.c +++ b/subsys/console/getchar.c @@ -20,7 +20,8 @@ int console_putchar(char c) u8_t console_getchar(void) { - return tty_getchar(&console_serial); + /* Console works in blocking mode, so we don't expect an error here */ + return (u8_t)tty_getchar(&console_serial); } void console_init(void) diff --git a/subsys/console/tty.c b/subsys/console/tty.c index 5f004f542bb3d..0d916c7d92c98 100644 --- a/subsys/console/tty.c +++ b/subsys/console/tty.c @@ -91,12 +91,16 @@ int tty_putchar(struct tty_serial *tty, u8_t c) return 0; } -u8_t tty_getchar(struct tty_serial *tty) +int tty_getchar(struct tty_serial *tty) { unsigned int key; u8_t c; + int res; - k_sem_take(&tty->rx_sem, K_FOREVER); + res = k_sem_take(&tty->rx_sem, tty->rx_timeout); + if (res < 0) { + return res; + } key = irq_lock(); c = tty->rx_ringbuf[tty->rx_get++]; @@ -120,6 +124,8 @@ void tty_init(struct tty_serial *tty, struct device *uart_dev, tty->rx_get = tty->rx_put = tty->tx_get = tty->tx_put = 0; k_sem_init(&tty->rx_sem, 0, UINT_MAX); + tty->rx_timeout = K_FOREVER; + uart_irq_callback_user_data_set(uart_dev, tty_uart_isr, tty); uart_irq_rx_enable(uart_dev); } From 224f1fe95eb5ac3a604670758ae745991dba7d29 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 23 Oct 2018 20:02:25 +0300 Subject: [PATCH 2/3] subsys: console: tty: Allow to specify transmit timeout Previously, transmit was effectively non-blocking - a character either went into buffer, or -1 was returned. Now it's possible to block if buffer is full. Timeout is K_FOREVER by default, can be adjusted with tty_set_tx_timeout() (similar to receive timeout). Signed-off-by: Paul Sokolovsky --- include/console.h | 16 ++++++++++++++++ subsys/console/tty.c | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/include/console.h b/include/console.h index 599036bdc7740..ce3425be2e476 100644 --- a/include/console.h +++ b/include/console.h @@ -23,9 +23,11 @@ struct tty_serial { u16_t rx_get, rx_put; s32_t rx_timeout; + struct k_sem tx_sem; u8_t *tx_ringbuf; u32_t tx_ringbuf_sz; u16_t tx_get, tx_put; + s32_t tx_timeout; }; /** @@ -62,6 +64,20 @@ static inline void tty_set_rx_timeout(struct tty_serial *tty, s32_t timeout) tty->rx_timeout = timeout; } +/** + * @brief Set transmit timeout for tty device. + * + * Set timeout for putchar() operation, for a case when output buffer is full. + * Default timeout after device initialization is K_FOREVER. + * + * @param tty tty device structure + * @param timeout timeout in milliseconds, or K_FOREVER, or K_NO_WAIT + */ +static inline void tty_set_tx_timeout(struct tty_serial *tty, s32_t timeout) +{ + tty->tx_timeout = timeout; +} + /** * @brief Input a character from a tty device. * diff --git a/subsys/console/tty.c b/subsys/console/tty.c index 0d916c7d92c98..d4fd5d48b6cc0 100644 --- a/subsys/console/tty.c +++ b/subsys/console/tty.c @@ -42,6 +42,7 @@ static void tty_uart_isr(void *user_data) if (tty->tx_get >= tty->tx_ringbuf_sz) { tty->tx_get = 0; } + k_sem_give(&tty->tx_sem); } } } @@ -72,6 +73,12 @@ int tty_putchar(struct tty_serial *tty, u8_t c) { unsigned int key; int tx_next; + int res; + + res = k_sem_take(&tty->tx_sem, tty->tx_timeout); + if (res < 0) { + return res; + } key = irq_lock(); tx_next = tty->tx_put + 1; @@ -123,8 +130,10 @@ void tty_init(struct tty_serial *tty, struct device *uart_dev, tty->tx_ringbuf_sz = txbuf_sz; tty->rx_get = tty->rx_put = tty->tx_get = tty->tx_put = 0; k_sem_init(&tty->rx_sem, 0, UINT_MAX); + k_sem_init(&tty->tx_sem, txbuf_sz - 1, UINT_MAX); tty->rx_timeout = K_FOREVER; + tty->tx_timeout = K_FOREVER; uart_irq_callback_user_data_set(uart_dev, tty_uart_isr, tty); uart_irq_rx_enable(uart_dev); From 7433a40d3140a5dd081e0f6984f79fc96197cc95 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 23 Oct 2018 21:15:46 +0300 Subject: [PATCH 3/3] subsys: console: Switch to POSIX-like read/write interface Tty device gets only read/write calls, but console retains getchar/putchar for convenience. Signed-off-by: Paul Sokolovsky --- include/console.h | 58 +++++++++++++++++++++++++++------- subsys/console/getchar.c | 29 ++++++++++++++--- subsys/console/tty.c | 67 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 135 insertions(+), 19 deletions(-) diff --git a/include/console.h b/include/console.h index ce3425be2e476..88e3df7c31410 100644 --- a/include/console.h +++ b/include/console.h @@ -7,6 +7,7 @@ #ifndef ZEPHYR_INCLUDE_CONSOLE_H_ #define ZEPHYR_INCLUDE_CONSOLE_H_ +#include #include #include @@ -79,24 +80,31 @@ static inline void tty_set_tx_timeout(struct tty_serial *tty, s32_t timeout) } /** - * @brief Input a character from a tty device. + * @brief Read data from a tty device. * * @param tty tty device structure - * @return >=0, an input character - * <0, an error (e.g. -EAGAIN if timeout expired) + * @param buf buffer to read data to + * @param size maximum number of bytes to read + * @return >0, number of actually read bytes (can be less than size param) + * =0, for EOF-like condition (e.g., break signalled) + * <0, in case of error (e.g. -EAGAIN if timeout expired). errno + * variable is also set. */ -int tty_getchar(struct tty_serial *tty); +ssize_t tty_read(struct tty_serial *tty, void *buf, size_t size); /** - * @brief Output a character from to tty device. + * @brief Write data to tty device. * * @param tty tty device structure - * @param c character to output - * @return 0 if ok, <0 if error + * @param buf buffer containg data + * @param size maximum number of bytes to write + * @return =>0, number of actually written bytes (can be less than size param) + * <0, in case of error (e.g. -EAGAIN if timeout expired). errno + * variable is also set. */ -int tty_putchar(struct tty_serial *tty, u8_t c); +ssize_t tty_write(struct tty_serial *tty, const void *buf, size_t size); -/** @brief Initialize console_getchar()/putchar() calls. +/** @brief Initialize console device. * * This function should be called once to initialize pull-style * access to console via console_getchar() function and buffered @@ -108,6 +116,31 @@ int tty_putchar(struct tty_serial *tty, u8_t c); */ void console_init(void); +/** + * @brief Read data from console. + * + * @param dummy ignored, present to follow read() prototype + * @param buf buffer to read data to + * @param size maximum number of bytes to read + * @return >0, number of actually read bytes (can be less than size param) + * =0, in case of EOF + * <0, in case of error (e.g. -EAGAIN if timeout expired). errno + * variable is also set. + */ +ssize_t console_read(void *dummy, void *buf, size_t size); + +/** + * @brief Write data to console. + * + * @param dummy ignored, present to follow write() prototype + * @param buf buffer to write data to + * @param size maximum number of bytes to write + * @return =>0, number of actually written bytes (can be less than size param) + * <0, in case of error (e.g. -EAGAIN if timeout expired). errno + * variable is also set. + */ +ssize_t console_write(void *dummy, const void *buf, size_t size); + /** @brief Get next char from console input buffer. * * Return next input character from console. If no characters available, @@ -118,16 +151,17 @@ void console_init(void); * Zephyr callback-based console input processing, shell subsystem, * or console_getline(). * - * @return A character read, including control characters. + * @return 0-255: a character read, including control characters. + * <0: error occured. */ -u8_t console_getchar(void); +int console_getchar(void); /** @brief Output a char to console (buffered). * * Puts a character into console output buffer. It will be sent * to a console asynchronously, e.g. using an IRQ handler. * - * @return -1 on output buffer overflow, otherwise 0. + * @return <0 on error, otherwise 0. */ int console_putchar(char c); diff --git a/subsys/console/getchar.c b/subsys/console/getchar.c index 6b313563be566..aa4d9085ee27a 100644 --- a/subsys/console/getchar.c +++ b/subsys/console/getchar.c @@ -13,15 +13,36 @@ static struct tty_serial console_serial; static u8_t console_rxbuf[CONFIG_CONSOLE_GETCHAR_BUFSIZE]; static u8_t console_txbuf[CONFIG_CONSOLE_PUTCHAR_BUFSIZE]; +ssize_t console_write(void *dummy, const void *buf, size_t size) +{ + ARG_UNUSED(dummy); + + return tty_write(&console_serial, buf, size); +} + +ssize_t console_read(void *dummy, void *buf, size_t size) +{ + ARG_UNUSED(dummy); + + return tty_read(&console_serial, buf, size); +} + int console_putchar(char c) { - return tty_putchar(&console_serial, (u8_t)c); + return tty_write(&console_serial, &c, 1); } -u8_t console_getchar(void) +int console_getchar(void) { - /* Console works in blocking mode, so we don't expect an error here */ - return (u8_t)tty_getchar(&console_serial); + u8_t c; + int res; + + res = tty_read(&console_serial, &c, 1); + if (res < 0) { + return res; + } + + return c; } void console_init(void) diff --git a/subsys/console/tty.c b/subsys/console/tty.c index d4fd5d48b6cc0..31b475a909d7b 100644 --- a/subsys/console/tty.c +++ b/subsys/console/tty.c @@ -69,7 +69,7 @@ static int tty_irq_input_hook(struct tty_serial *tty, u8_t c) return 1; } -int tty_putchar(struct tty_serial *tty, u8_t c) +static int tty_putchar(struct tty_serial *tty, u8_t c) { unsigned int key; int tx_next; @@ -87,7 +87,7 @@ int tty_putchar(struct tty_serial *tty, u8_t c) } if (tx_next == tty->tx_get) { irq_unlock(key); - return -1; + return -ENOSPC; } tty->tx_ringbuf[tty->tx_put] = c; @@ -98,7 +98,37 @@ int tty_putchar(struct tty_serial *tty, u8_t c) return 0; } -int tty_getchar(struct tty_serial *tty) +ssize_t tty_write(struct tty_serial *tty, const void *buf, size_t size) +{ + const u8_t *p = buf; + size_t out_size = 0; + int res = 0; + + while (size--) { + res = tty_putchar(tty, *p++); + if (res < 0) { + /* If we didn't transmit anything, return the error. */ + if (out_size == 0) { + errno = -res; + return res; + } + + /* + * Otherwise, return how much we transmitted. If error + * was transient (like EAGAIN), on next call user might + * not even get it. And if it's non-transient, they'll + * get it on the next call. + */ + return out_size; + } + + out_size++; + } + + return out_size; +} + +static int tty_getchar(struct tty_serial *tty) { unsigned int key; u8_t c; @@ -119,6 +149,37 @@ int tty_getchar(struct tty_serial *tty) return c; } +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; + + while (size--) { + res = tty_getchar(tty); + if (res < 0) { + /* If we didn't transmit anything, return the error. */ + if (out_size == 0) { + errno = -res; + return res; + } + + /* + * Otherwise, return how much we transmitted. If error + * was transient (like EAGAIN), on next call user might + * not even get it. And if it's non-transient, they'll + * get it on the next call. + */ + return out_size; + } + + *p++ = (u8_t)res; + out_size++; + } + + return out_size; +} + void tty_init(struct tty_serial *tty, struct device *uart_dev, u8_t *rxbuf, u16_t rxbuf_sz, u8_t *txbuf, u16_t txbuf_sz)