1 /*
2 * Copyright (c) 2021 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/shell/shell.h>
7 #include <zephyr/shell/shell_uart.h>
8 #include <zephyr/drivers/uart.h>
9 #include <zephyr/device.h>
10
shell_init_from_work(struct k_work * work)11 void shell_init_from_work(struct k_work *work)
12 {
13 const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
14 bool log_backend = CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > 0;
15 uint32_t level =
16 (CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > LOG_LEVEL_DBG) ?
17 CONFIG_LOG_MAX_LEVEL : CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL;
18
19 shell_init(shell_backend_uart_get_ptr(), dev,
20 shell_backend_uart_get_ptr()->ctx->cfg.flags,
21 log_backend, level);
22 }
23
shell_reinit_trigger(void)24 static void shell_reinit_trigger(void)
25 {
26 static struct k_work shell_init_work;
27
28 k_work_init(&shell_init_work, shell_init_from_work);
29 int err = k_work_submit(&shell_init_work);
30
31 (void)err;
32 __ASSERT_NO_MSG(err >= 0);
33 }
34
direct_uart_callback(const struct device * dev,void * user_data)35 static void direct_uart_callback(const struct device *dev, void *user_data)
36 {
37 static uint8_t buf[1];
38 static bool tx_busy;
39
40 uart_irq_update(dev);
41
42
43 if (uart_irq_rx_ready(dev)) {
44 while (uart_fifo_read(dev, buf, sizeof(buf))) {
45 if (!tx_busy) {
46 uart_irq_tx_enable(dev);
47 }
48 }
49 }
50
51 if (uart_irq_tx_ready(dev)) {
52 if (!tx_busy) {
53 (void)uart_fifo_fill(dev, buf, sizeof(buf));
54 tx_busy = true;
55 } else {
56 tx_busy = false;
57 uart_irq_tx_disable(dev);
58 if (buf[0] == 'x') {
59 uart_irq_rx_disable(dev);
60 shell_reinit_trigger();
61 }
62 }
63 }
64 }
65
uart_poll_timer_stopped(struct k_timer * timer)66 static void uart_poll_timer_stopped(struct k_timer *timer)
67 {
68 shell_reinit_trigger();
69 }
70
uart_poll_timeout(struct k_timer * timer)71 static void uart_poll_timeout(struct k_timer *timer)
72 {
73 char c;
74 const struct device *dev = k_timer_user_data_get(timer);
75
76 while (uart_poll_in(dev, &c) == 0) {
77 if (c != 'x') {
78 uart_poll_out(dev, c);
79 } else {
80 k_timer_stop(timer);
81 }
82 }
83 }
84
85 K_TIMER_DEFINE(uart_poll_timer, uart_poll_timeout, uart_poll_timer_stopped);
86
shell_uninit_cb(const struct shell * sh,int res)87 static void shell_uninit_cb(const struct shell *sh, int res)
88 {
89 __ASSERT_NO_MSG(res >= 0);
90 const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
91
92 if (IS_ENABLED(CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN)) {
93 /* connect uart to my handler */
94 uart_irq_callback_user_data_set(dev, direct_uart_callback, NULL);
95 uart_irq_rx_enable(dev);
96 } else {
97 k_timer_user_data_set(&uart_poll_timer, (void *)dev);
98 k_timer_start(&uart_poll_timer, K_MSEC(10), K_MSEC(10));
99 }
100 }
101
cmd_uart_release(const struct shell * sh,size_t argc,char ** argv)102 static int cmd_uart_release(const struct shell *sh, size_t argc, char **argv)
103 {
104 ARG_UNUSED(argc);
105 ARG_UNUSED(argv);
106
107 if (sh != shell_backend_uart_get_ptr()) {
108 shell_error(sh, "Command dedicated for shell over uart");
109 return -EINVAL;
110 }
111
112 shell_print(sh, "Uninitializing shell, use 'x' to reinitialize");
113 shell_uninit(sh, shell_uninit_cb);
114
115 return 0;
116 }
117
118 SHELL_CMD_REGISTER(shell_uart_release, NULL,
119 "Uninitialize shell instance and release uart, start loopback "
120 "on uart. Shell instance is reinitialized when 'x' is pressed",
121 cmd_uart_release);
122