diff options
Diffstat (limited to 'zephyr/shim/src/console.c')
-rw-r--r-- | zephyr/shim/src/console.c | 156 |
1 files changed, 129 insertions, 27 deletions
diff --git a/zephyr/shim/src/console.c b/zephyr/shim/src/console.c index c4311761f3..22272a9c8d 100644 --- a/zephyr/shim/src/console.c +++ b/zephyr/shim/src/console.c @@ -5,53 +5,152 @@ #include <device.h> #include <drivers/uart.h> -#include <init.h> -#include <kernel.h> #include <shell/shell.h> #include <shell/shell_uart.h> #include <stdbool.h> #include <string.h> #include <sys/printk.h> +#include <sys/ring_buffer.h> #include <zephyr.h> #include "console.h" #include "printf.h" #include "uart.h" -static const struct device *uart_dev; -#ifdef CONFIG_UART_CONSOLE_ON_DEV_NAME -static int init_uart_dev(const struct device *unused) +static struct k_poll_signal shell_uninit_signal; +static struct k_poll_signal shell_init_signal; +RING_BUF_DECLARE(rx_buffer, CONFIG_UART_RX_BUF_SIZE); + +static void uart_rx_handle(const struct device *dev) { - ARG_UNUSED(unused); - uart_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME); - return 0; + static uint8_t scratch; + static uint8_t *data; + static uint32_t len, rd_len; + + do { + /* Get some bytes on the ring buffer */ + len = ring_buf_put_claim(&rx_buffer, &data, rx_buffer.size); + if (len > 0) { + /* Read from the FIFO up to `len` bytes */ + rd_len = uart_fifo_read(dev, data, len); + + /* Put `rd_len` bytes on the ring buffer */ + ring_buf_put_finish(&rx_buffer, rd_len); + } else { + /* There's no room on the ring buffer, throw away 1 + * byte. + */ + rd_len = uart_fifo_read(dev, &scratch, 1); + } + } while (rd_len != 0 && rd_len == len); } -SYS_INIT(init_uart_dev, POST_KERNEL, 50); -#endif -void uart_shell_stop(void) +static void uart_callback(const struct device *dev, void *user_data) { - /* Disable interrupts for the uart. */ - if (uart_dev) { - uart_irq_tx_disable(uart_dev); - uart_irq_rx_disable(uart_dev); + uart_irq_update(dev); + + if (uart_irq_rx_ready(dev)) + uart_rx_handle(dev); +} + +static void shell_uninit_callback(const struct shell *shell, int res) +{ + const struct device *dev = + device_get_binding(CONFIG_UART_SHELL_ON_DEV_NAME); + + if (!res) { + /* Set the new callback */ + uart_irq_callback_user_data_set(dev, uart_callback, NULL); + + /* Disable TX interrupts. We don't actually use TX but for some + * reason none of this works without this line. + */ + uart_irq_tx_disable(dev); + + /* Enable RX interrupts */ + uart_irq_rx_enable(dev); } - /* Stop the shell and process all pending operations. */ - shell_stop(shell_backend_uart_get_ptr()); - shell_process(shell_backend_uart_get_ptr()); + /* Notify the uninit signal that we finished */ + k_poll_signal_raise(&shell_uninit_signal, res); } -void uart_shell_start(void) +int uart_shell_stop(void) { - /* Restart the shell. */ - shell_start(shell_backend_uart_get_ptr()); + struct k_poll_event event = K_POLL_EVENT_INITIALIZER( + K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, + &shell_uninit_signal); + const struct device *dev = + device_get_binding(CONFIG_UART_SHELL_ON_DEV_NAME); + + /* Clear all pending input */ + uart_clear_input(); + + /* Disable RX and TX interrupts */ + uart_irq_rx_disable(dev); + uart_irq_tx_disable(dev); + + /* Initialize the uninit signal */ + k_poll_signal_init(&shell_uninit_signal); + + /* Stop the shell */ + shell_uninit(shell_backend_uart_get_ptr(), shell_uninit_callback); - /* Re-enable interrupts for the uart. */ - if (uart_dev) { - uart_irq_rx_enable(uart_dev); - uart_irq_tx_enable(uart_dev); + /* Wait for the shell to be turned off, the signal will wake us */ + k_poll(&event, 1, K_FOREVER); + + /* Event was signaled, return the result */ + return event.signal->result; +} + +static void shell_init_from_work(struct k_work *work) +{ + const struct device *dev = + device_get_binding(CONFIG_UART_SHELL_ON_DEV_NAME); + bool log_backend = CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > 0; + uint32_t level; + ARG_UNUSED(work); + + if (CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > LOG_LEVEL_DBG) { + level = CONFIG_LOG_MAX_LEVEL; + } else { + level = CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL; } + + /* Initialize the shell and re-enable both RX and TX */ + shell_init(shell_backend_uart_get_ptr(), dev, false, log_backend, + level); + uart_irq_rx_enable(dev); + uart_irq_tx_enable(dev); + + /* Notify the init signal that initialization is complete */ + k_poll_signal_raise(&shell_init_signal, 0); +} + +void uart_shell_start(void) +{ + static struct k_work shell_init_work; + const struct device *dev = + device_get_binding(CONFIG_UART_SHELL_ON_DEV_NAME); + struct k_poll_event event = K_POLL_EVENT_INITIALIZER( + K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, + &shell_init_signal); + + /* Disable RX and TX interrupts */ + uart_irq_rx_disable(dev); + uart_irq_tx_disable(dev); + + /* Initialize k_work to call shell init (this makes it thread safe) */ + k_work_init(&shell_init_work, shell_init_from_work); + + /* Initialize the init signal to make sure we're read to listen */ + k_poll_signal_init(&shell_init_signal); + + /* Submit the work to be run by the kernel */ + k_work_submit(&shell_init_work); + + /* Wait for initialization to be run, the signal will wake us */ + k_poll(&event, 1, K_FOREVER); } int zshim_run_ec_console_command(int (*handler)(int argc, char **argv), @@ -136,12 +235,15 @@ int uart_getc(void) { uint8_t c; - if (uart_dev && !uart_poll_in(uart_dev, &c)) + if (ring_buf_get(&rx_buffer, &c, 1)) { return c; + } return -1; } void uart_clear_input(void) { - /* Not needed since we're not stopping the shell. */ + /* Clear any remaining shell processing. */ + shell_process(shell_backend_uart_get_ptr()); + ring_buf_reset(&rx_buffer); } |