1 /*
2  * Copyright (c) 2018, Oticon A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT zephyr_native_posix_uart
8 
9 #include <stdbool.h>
10 
11 #include <zephyr/drivers/uart.h>
12 #include <zephyr/kernel.h>
13 
14 #include "cmdline.h" /* native_posix command line options header */
15 #include "posix_native_task.h"
16 #include "uart_native_ptty_bottom.h"
17 #include "nsi_host_trampolines.h"
18 
19 /*
20  * UART driver for POSIX ARCH based boards.
21  * It can support up to two UARTs.
22  *
23  * For the first UART:
24  *
25  * It can either be connected to the process STDIN+STDOUT
26  * OR
27  * to a dedicated pseudo terminal
28  *
29  * The 2nd option is the recommended one for interactive use, as the pseudo
30  * terminal driver will be configured in "raw" mode, and will therefore behave
31  * more like a real UART.
32  *
33  * When connected to its own pseudo terminal, it may also auto attach a terminal
34  * emulator to it, if set so from command line.
35  */
36 
37 static int np_uart_stdin_poll_in(const struct device *dev,
38 				 unsigned char *p_char);
39 static int np_uart_tty_poll_in(const struct device *dev,
40 			       unsigned char *p_char);
41 static void np_uart_poll_out(const struct device *dev,
42 				      unsigned char out_char);
43 
44 static bool auto_attach;
45 static bool wait_pts;
46 static char *auto_attach_cmd = CONFIG_NATIVE_UART_AUTOATTACH_DEFAULT_CMD;
47 
48 struct native_uart_status {
49 	int out_fd; /* File descriptor used for output */
50 	int in_fd; /* File descriptor used for input */
51 };
52 
53 static struct native_uart_status native_uart_status_0;
54 
55 static struct uart_driver_api np_uart_driver_api_0 = {
56 	.poll_out = np_uart_poll_out,
57 	.poll_in = np_uart_tty_poll_in,
58 };
59 
60 #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE)
61 static struct native_uart_status native_uart_status_1;
62 
63 static struct uart_driver_api np_uart_driver_api_1 = {
64 	.poll_out = np_uart_poll_out,
65 	.poll_in = np_uart_tty_poll_in,
66 };
67 #endif /* CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE */
68 
69 #define ERROR posix_print_error_and_exit
70 #define WARN posix_print_warning
71 
72 
73 
74 /**
75  * @brief Initialize the first native_posix serial port
76  *
77  * @param dev UART_0 device struct
78  *
79  * @return 0 (if it fails catastrophically, the execution is terminated)
80  */
np_uart_0_init(const struct device * dev)81 static int np_uart_0_init(const struct device *dev)
82 {
83 	struct native_uart_status *d;
84 
85 	d = (struct native_uart_status *)dev->data;
86 
87 	if (IS_ENABLED(CONFIG_NATIVE_UART_0_ON_OWN_PTY)) {
88 		int tty_fn = np_uart_open_ptty(dev->name, auto_attach_cmd, auto_attach, wait_pts);
89 
90 		d->in_fd = tty_fn;
91 		d->out_fd = tty_fn;
92 		np_uart_driver_api_0.poll_in = np_uart_tty_poll_in;
93 	} else { /* NATIVE_UART_0_ON_STDINOUT */
94 		d->in_fd  = np_uart_ptty_get_stdin_fileno();
95 		d->out_fd = np_uart_ptty_get_stdout_fileno();
96 		np_uart_driver_api_0.poll_in = np_uart_stdin_poll_in;
97 	}
98 
99 	return 0;
100 }
101 
102 #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE)
103 /*
104  * Initialize the 2nd UART port.
105  * This port will be always attached to its own new pseudoterminal.
106  */
np_uart_1_init(const struct device * dev)107 static int np_uart_1_init(const struct device *dev)
108 {
109 	struct native_uart_status *d;
110 	int tty_fn;
111 
112 	d = (struct native_uart_status *)dev->data;
113 
114 	tty_fn = np_uart_open_ptty(dev->name, NULL, false, wait_pts);
115 
116 	d->in_fd = tty_fn;
117 	d->out_fd = tty_fn;
118 
119 	return 0;
120 }
121 #endif
122 
123 /*
124  * @brief Output a character towards the serial port
125  *
126  * @param dev UART device struct
127  * @param out_char Character to send.
128  */
np_uart_poll_out(const struct device * dev,unsigned char out_char)129 static void np_uart_poll_out(const struct device *dev,
130 				      unsigned char out_char)
131 {
132 	int ret;
133 	struct native_uart_status *d = (struct native_uart_status *)dev->data;
134 
135 	if (wait_pts) {
136 
137 		while (1) {
138 			int rc = np_uart_slave_connected(d->out_fd);
139 
140 			if (rc == 1) {
141 				break;
142 			}
143 			k_sleep(K_MSEC(100));
144 		}
145 	}
146 
147 	/* The return value of write() cannot be ignored (there is a warning)
148 	 * but we do not need the return value for anything.
149 	 */
150 	ret = nsi_host_write(d->out_fd, &out_char, 1);
151 	(void) ret;
152 }
153 
154 /**
155  * @brief Poll the device for input.
156  *
157  * @param dev UART device structure.
158  * @param p_char Pointer to character.
159  *
160  * @retval 0 If a character arrived and was stored in p_char
161  * @retval -1 If no character was available to read
162  */
np_uart_stdin_poll_in(const struct device * dev,unsigned char * p_char)163 static int np_uart_stdin_poll_in(const struct device *dev,
164 				 unsigned char *p_char)
165 {
166 	int in_f = ((struct native_uart_status *)dev->data)->in_fd;
167 	static bool disconnected;
168 	int rc;
169 
170 	if (disconnected == true) {
171 		return -1;
172 	}
173 
174 	rc = np_uart_stdin_poll_in_bottom(in_f, p_char);
175 	if (rc == -2) {
176 		disconnected = true;
177 		return -1;
178 	}
179 
180 	return rc;
181 }
182 
183 /**
184  * @brief Poll the device for input.
185  *
186  * @param dev UART device structure.
187  * @param p_char Pointer to character.
188  *
189  * @retval 0 If a character arrived and was stored in p_char
190  * @retval -1 If no character was available to read
191  */
np_uart_tty_poll_in(const struct device * dev,unsigned char * p_char)192 static int np_uart_tty_poll_in(const struct device *dev,
193 			       unsigned char *p_char)
194 {
195 	int n = -1;
196 	int in_f = ((struct native_uart_status *)dev->data)->in_fd;
197 
198 	n = nsi_host_read(in_f, p_char, 1);
199 	if (n == -1) {
200 		return -1;
201 	}
202 	return 0;
203 }
204 
205 DEVICE_DT_INST_DEFINE(0,
206 	    np_uart_0_init, NULL,
207 	    (void *)&native_uart_status_0, NULL,
208 	    PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY,
209 	    &np_uart_driver_api_0);
210 
211 #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE)
212 DEVICE_DT_INST_DEFINE(1,
213 	    np_uart_1_init, NULL,
214 	    (void *)&native_uart_status_1, NULL,
215 	    PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY,
216 	    &np_uart_driver_api_1);
217 #endif /* CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE */
218 
auto_attach_cmd_cb(char * argv,int offset)219 static void auto_attach_cmd_cb(char *argv, int offset)
220 {
221 	auto_attach_cmd = &argv[offset];
222 	auto_attach = true;
223 }
224 
np_add_uart_options(void)225 static void np_add_uart_options(void)
226 {
227 	if (!IS_ENABLED(CONFIG_NATIVE_UART_0_ON_OWN_PTY)) {
228 		return;
229 	}
230 
231 	static struct args_struct_t uart_options[] = {
232 	{
233 		.is_switch = true,
234 		.option = "attach_uart",
235 		.type = 'b',
236 		.dest = (void *)&auto_attach,
237 		.descript = "Automatically attach to the UART terminal"
238 	},
239 	{
240 		.option = "attach_uart_cmd",
241 		.name = "\"cmd\"",
242 		.type = 's',
243 		.call_when_found = auto_attach_cmd_cb,
244 		.descript = "Command used to automatically attach to the terminal (implies "
245 			    "auto_attach), by default: "
246 			    "'" CONFIG_NATIVE_UART_AUTOATTACH_DEFAULT_CMD "'"
247 	},
248 	IF_ENABLED(CONFIG_UART_NATIVE_WAIT_PTS_READY_ENABLE, (
249 		{
250 		.is_switch = true,
251 		.option = "wait_uart",
252 		.type = 'b',
253 		.dest = (void *)&wait_pts,
254 		.descript = "Hold writes to the uart/pts until a client is connected/ready"
255 		},
256 	))
257 	ARG_TABLE_ENDMARKER
258 	};
259 
260 	native_add_command_line_opts(uart_options);
261 }
262 
np_cleanup_uart(void)263 static void np_cleanup_uart(void)
264 {
265 	if (IS_ENABLED(CONFIG_NATIVE_UART_0_ON_OWN_PTY)) {
266 		if (native_uart_status_0.in_fd != 0) {
267 			nsi_host_close(native_uart_status_0.in_fd);
268 		}
269 	}
270 
271 #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE)
272 	if (native_uart_status_1.in_fd != 0) {
273 		nsi_host_close(native_uart_status_1.in_fd);
274 	}
275 #endif
276 }
277 
278 NATIVE_TASK(np_add_uart_options, PRE_BOOT_1, 11);
279 NATIVE_TASK(np_cleanup_uart, ON_EXIT, 99);
280