1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "platform-simulation.h"
30
31 #include <assert.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <poll.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <termios.h>
39 #include <unistd.h>
40
41 #include <openthread/platform/debug_uart.h>
42
43 #include "utils/code_utils.h"
44 #include "utils/uart.h"
45
46 #if OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART == 0
47
48 static uint8_t s_receive_buffer[128];
49 static const uint8_t *s_write_buffer;
50 static uint16_t s_write_length;
51 static int s_in_fd;
52 static int s_out_fd;
53
54 static struct termios original_stdin_termios;
55 static struct termios original_stdout_termios;
56
restore_stdin_termios(void)57 static void restore_stdin_termios(void)
58 {
59 tcsetattr(s_in_fd, TCSAFLUSH, &original_stdin_termios);
60 }
61
restore_stdout_termios(void)62 static void restore_stdout_termios(void)
63 {
64 tcsetattr(s_out_fd, TCSAFLUSH, &original_stdout_termios);
65 }
66
platformUartRestore(void)67 void platformUartRestore(void)
68 {
69 restore_stdin_termios();
70 restore_stdout_termios();
71 dup2(s_out_fd, STDOUT_FILENO);
72 }
73
otPlatUartEnable(void)74 otError otPlatUartEnable(void)
75 {
76 otError error = OT_ERROR_NONE;
77 struct termios termios;
78
79 s_in_fd = dup(STDIN_FILENO);
80 s_out_fd = dup(STDOUT_FILENO);
81 dup2(STDERR_FILENO, STDOUT_FILENO);
82
83 // We need this signal to make sure that this
84 // process terminates properly.
85 signal(SIGPIPE, SIG_DFL);
86
87 if (isatty(s_in_fd))
88 {
89 tcgetattr(s_in_fd, &original_stdin_termios);
90 atexit(&restore_stdin_termios);
91 }
92
93 if (isatty(s_out_fd))
94 {
95 tcgetattr(s_out_fd, &original_stdout_termios);
96 atexit(&restore_stdout_termios);
97 }
98
99 if (isatty(s_in_fd))
100 {
101 // get current configuration
102 otEXPECT_ACTION(tcgetattr(s_in_fd, &termios) == 0, perror("tcgetattr"); error = OT_ERROR_GENERIC);
103
104 // Set up the termios settings for raw mode. This turns
105 // off input/output processing, line processing, and character processing.
106 cfmakeraw(&termios);
107
108 // Set up our cflags for local use. Turn on hangup-on-close.
109 termios.c_cflag |= HUPCL | CREAD | CLOCAL;
110
111 // "Minimum number of characters for noncanonical read"
112 termios.c_cc[VMIN] = 1;
113
114 // "Timeout in deciseconds for noncanonical read"
115 termios.c_cc[VTIME] = 0;
116
117 // configure baud rate
118 otEXPECT_ACTION(cfsetispeed(&termios, OPENTHREAD_SIMULATION_UART_BAUDRATE) == 0, perror("cfsetispeed");
119 error = OT_ERROR_GENERIC);
120
121 // set configuration
122 otEXPECT_ACTION(tcsetattr(s_in_fd, TCSANOW, &termios) == 0, perror("tcsetattr"); error = OT_ERROR_GENERIC);
123 }
124
125 if (isatty(s_out_fd))
126 {
127 // get current configuration
128 otEXPECT_ACTION(tcgetattr(s_out_fd, &termios) == 0, perror("tcgetattr"); error = OT_ERROR_GENERIC);
129
130 // Set up the termios settings for raw mode. This turns
131 // off input/output processing, line processing, and character processing.
132 cfmakeraw(&termios);
133
134 // Absolutely obliterate all output processing.
135 termios.c_oflag = 0;
136
137 // Set up our cflags for local use. Turn on hangup-on-close.
138 termios.c_cflag |= HUPCL | CREAD | CLOCAL;
139
140 // configure baud rate
141 otEXPECT_ACTION(cfsetospeed(&termios, OPENTHREAD_SIMULATION_UART_BAUDRATE) == 0, perror("cfsetospeed");
142 error = OT_ERROR_GENERIC);
143
144 // set configuration
145 otEXPECT_ACTION(tcsetattr(s_out_fd, TCSANOW, &termios) == 0, perror("tcsetattr"); error = OT_ERROR_GENERIC);
146 }
147
148 return error;
149
150 exit:
151 close(s_in_fd);
152 close(s_out_fd);
153 return error;
154 }
155
otPlatUartDisable(void)156 otError otPlatUartDisable(void)
157 {
158 otError error = OT_ERROR_NONE;
159
160 close(s_in_fd);
161 close(s_out_fd);
162
163 return error;
164 }
165
otPlatUartSend(const uint8_t * aBuf,uint16_t aBufLength)166 otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength)
167 {
168 otError error = OT_ERROR_NONE;
169
170 otEXPECT_ACTION(s_write_length == 0, error = OT_ERROR_BUSY);
171
172 s_write_buffer = aBuf;
173 s_write_length = aBufLength;
174
175 exit:
176 return error;
177 }
178
platformUartUpdateFdSet(fd_set * aReadFdSet,fd_set * aWriteFdSet,fd_set * aErrorFdSet,int * aMaxFd)179 void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd)
180 {
181 if (aReadFdSet != NULL)
182 {
183 FD_SET(s_in_fd, aReadFdSet);
184
185 if (aErrorFdSet != NULL)
186 {
187 FD_SET(s_in_fd, aErrorFdSet);
188 }
189
190 if (aMaxFd != NULL && *aMaxFd < s_in_fd)
191 {
192 *aMaxFd = s_in_fd;
193 }
194 }
195
196 if ((aWriteFdSet != NULL) && (s_write_length > 0))
197 {
198 FD_SET(s_out_fd, aWriteFdSet);
199
200 if (aErrorFdSet != NULL)
201 {
202 FD_SET(s_out_fd, aErrorFdSet);
203 }
204
205 if (aMaxFd != NULL && *aMaxFd < s_out_fd)
206 {
207 *aMaxFd = s_out_fd;
208 }
209 }
210 }
211
otPlatUartFlush(void)212 otError otPlatUartFlush(void)
213 {
214 otError error = OT_ERROR_NONE;
215 ssize_t count;
216
217 otEXPECT_ACTION(s_write_buffer != NULL && s_write_length > 0, error = OT_ERROR_INVALID_STATE);
218
219 while ((count = write(s_out_fd, s_write_buffer, s_write_length)) > 0 && (s_write_length -= count) > 0)
220 {
221 s_write_buffer += count;
222 }
223
224 if (count != -1)
225 {
226 assert(s_write_length == 0);
227 s_write_buffer = NULL;
228 }
229 else
230 {
231 perror("write(UART)");
232 exit(EXIT_FAILURE);
233 }
234
235 exit:
236 return error;
237 }
238
platformUartProcess(void)239 void platformUartProcess(void)
240 {
241 ssize_t rval;
242 const int error_flags = POLLERR | POLLNVAL | POLLHUP;
243 struct pollfd pollfd[] = {
244 {s_in_fd, POLLIN | error_flags, 0},
245 {s_out_fd, POLLOUT | error_flags, 0},
246 };
247
248 errno = 0;
249
250 rval = poll(pollfd, sizeof(pollfd) / sizeof(*pollfd), 0);
251
252 if (rval < 0)
253 {
254 perror("poll");
255 exit(EXIT_FAILURE);
256 }
257
258 if (rval > 0)
259 {
260 if ((pollfd[0].revents & error_flags) != 0)
261 {
262 perror("s_in_fd");
263 exit(EXIT_FAILURE);
264 }
265
266 if ((pollfd[1].revents & error_flags) != 0)
267 {
268 perror("s_out_fd");
269 exit(EXIT_FAILURE);
270 }
271
272 if (pollfd[0].revents & POLLIN)
273 {
274 rval = read(s_in_fd, s_receive_buffer, sizeof(s_receive_buffer));
275
276 if (rval <= 0)
277 {
278 perror("read");
279 exit(EXIT_FAILURE);
280 }
281
282 otPlatUartReceived(s_receive_buffer, (uint16_t)rval);
283 }
284
285 if ((s_write_length > 0) && (pollfd[1].revents & POLLOUT))
286 {
287 rval = write(s_out_fd, s_write_buffer, s_write_length);
288
289 if (rval >= 0)
290 {
291 s_write_buffer += (uint16_t)rval;
292 s_write_length -= (uint16_t)rval;
293
294 if (s_write_length == 0)
295 {
296 otPlatUartSendDone();
297 }
298 }
299 else if (errno != EINTR)
300 {
301 perror("write");
302 exit(EXIT_FAILURE);
303 }
304 }
305 }
306 }
307 #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART == 0
308
309 #if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART && (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART)
310
311 static FILE *posix_logfile;
312
otPlatDebugUart_logfile(const char * filename)313 otError otPlatDebugUart_logfile(const char *filename)
314 {
315 posix_logfile = fopen(filename, "wt");
316
317 return posix_logfile ? OT_ERROR_NONE : OT_ERROR_FAILED;
318 }
319
otPlatDebugUart_putchar_raw(int c)320 void otPlatDebugUart_putchar_raw(int c)
321 {
322 FILE *fp;
323
324 /* note: log file will have a mix of cr/lf and
325 * in some/many cases duplicate cr because in
326 * some cases the log function {ie: Mbed} already
327 * includes the CR or LF... but other log functions
328 * do not include cr/lf and expect it appended
329 */
330 fp = posix_logfile;
331
332 if (fp != NULL)
333 {
334 /* log is lost ... until a file is setup */
335 fputc(c, fp);
336 /* we could "fflush" but will not */
337 }
338 }
339
otPlatDebugUart_kbhit(void)340 int otPlatDebugUart_kbhit(void)
341 {
342 /* not supported */
343 return 0;
344 }
345
otPlatDebugUart_getc(void)346 int otPlatDebugUart_getc(void)
347 {
348 /* not supported */
349 return -1;
350 }
351
352 #endif
353