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 DEVICE_API(uart, np_uart_driver_api_0) = {
56 .poll_out = np_uart_poll_out,
57 #if defined(CONFIG_NATIVE_UART_0_ON_OWN_PTY)
58 .poll_in = np_uart_tty_poll_in,
59 #else
60 .poll_in = np_uart_stdin_poll_in,
61 #endif
62 };
63
64 #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE)
65 static struct native_uart_status native_uart_status_1;
66
67 static DEVICE_API(uart, np_uart_driver_api_1) = {
68 .poll_out = np_uart_poll_out,
69 .poll_in = np_uart_tty_poll_in,
70 };
71 #endif /* CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE */
72
73 #define ERROR posix_print_error_and_exit
74 #define WARN posix_print_warning
75
76
77
78 /**
79 * @brief Initialize the first native_posix serial port
80 *
81 * @param dev UART_0 device struct
82 *
83 * @return 0 (if it fails catastrophically, the execution is terminated)
84 */
np_uart_0_init(const struct device * dev)85 static int np_uart_0_init(const struct device *dev)
86 {
87 struct native_uart_status *d;
88
89 d = (struct native_uart_status *)dev->data;
90
91 if (IS_ENABLED(CONFIG_NATIVE_UART_0_ON_OWN_PTY)) {
92 int tty_fn = np_uart_open_ptty(dev->name, auto_attach_cmd, auto_attach, wait_pts);
93
94 d->in_fd = tty_fn;
95 d->out_fd = tty_fn;
96 } else { /* NATIVE_UART_0_ON_STDINOUT */
97 d->in_fd = np_uart_ptty_get_stdin_fileno();
98 d->out_fd = np_uart_ptty_get_stdout_fileno();
99 }
100
101 return 0;
102 }
103
104 #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE)
105 /*
106 * Initialize the 2nd UART port.
107 * This port will be always attached to its own new pseudoterminal.
108 */
np_uart_1_init(const struct device * dev)109 static int np_uart_1_init(const struct device *dev)
110 {
111 struct native_uart_status *d;
112 int tty_fn;
113
114 d = (struct native_uart_status *)dev->data;
115
116 tty_fn = np_uart_open_ptty(dev->name, NULL, false, wait_pts);
117
118 d->in_fd = tty_fn;
119 d->out_fd = tty_fn;
120
121 return 0;
122 }
123 #endif
124
125 /*
126 * @brief Output a character towards the serial port
127 *
128 * @param dev UART device struct
129 * @param out_char Character to send.
130 */
np_uart_poll_out(const struct device * dev,unsigned char out_char)131 static void np_uart_poll_out(const struct device *dev,
132 unsigned char out_char)
133 {
134 int ret;
135 struct native_uart_status *d = (struct native_uart_status *)dev->data;
136
137 if (wait_pts) {
138
139 while (1) {
140 int rc = np_uart_slave_connected(d->out_fd);
141
142 if (rc == 1) {
143 break;
144 }
145 k_sleep(K_MSEC(100));
146 }
147 }
148
149 /* The return value of write() cannot be ignored (there is a warning)
150 * but we do not need the return value for anything.
151 */
152 ret = nsi_host_write(d->out_fd, &out_char, 1);
153 (void) ret;
154 }
155
156 /**
157 * @brief Poll the device for input.
158 *
159 * @param dev UART device structure.
160 * @param p_char Pointer to character.
161 *
162 * @retval 0 If a character arrived and was stored in p_char
163 * @retval -1 If no character was available to read
164 */
np_uart_stdin_poll_in(const struct device * dev,unsigned char * p_char)165 static int __maybe_unused np_uart_stdin_poll_in(const struct device *dev, unsigned char *p_char)
166 {
167 int in_f = ((struct native_uart_status *)dev->data)->in_fd;
168 static bool disconnected;
169 int rc;
170
171 if (disconnected == true) {
172 return -1;
173 }
174
175 rc = np_uart_stdin_poll_in_bottom(in_f, p_char);
176 if (rc == -2) {
177 disconnected = true;
178 return -1;
179 }
180
181 return rc;
182 }
183
184 /**
185 * @brief Poll the device for input.
186 *
187 * @param dev UART device structure.
188 * @param p_char Pointer to character.
189 *
190 * @retval 0 If a character arrived and was stored in p_char
191 * @retval -1 If no character was available to read
192 */
np_uart_tty_poll_in(const struct device * dev,unsigned char * p_char)193 static int __maybe_unused np_uart_tty_poll_in(const struct device *dev, 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