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