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