tty: Support unbuffered operation to extend usecase coverage
The whole "tty" concept is conceived around efficient interrupt-driven operation. However, it's beneficial to add non interupt-driven operation under the same API: 1. Wider usecase coverage in general. 2. Allows to use the same familiar API (based on POSIX concepts) even for UART implementations without interrupt support. 3. Allows to switch operation dynamically based on the needs. For example, if the system is in degraded mode and interrupt handling cannot be trusted/disabled, allows to still output diagnostic information to user. This was the original motivation to provide such a mode, to support logging subsystem's "panic" mode. To implement this feature, tty_set_rx_buf() and tty_set_tx_buf() functions are provided, allowing to reconfigure buffers used dynamically. If configured buffer length is 0, the operation switched to unbuffered. Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
This commit is contained in:
parent
fe2f30ad4c
commit
a7df3a1e09
2 changed files with 119 additions and 3 deletions
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue