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 "simul_utils.h"
44 #include "lib/platform/exit_code.h"
45 #include "utils/code_utils.h"
46 #include "utils/uart.h"
47
48 #if OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART == 0
49
50 static uint8_t s_receive_buffer[128];
51 static const uint8_t *s_write_buffer;
52 static uint16_t s_write_length;
53 static int s_in_fd;
54 static int s_out_fd;
55
56 static struct termios original_stdin_termios;
57 static struct termios original_stdout_termios;
58
restore_stdin_termios(void)59 static void restore_stdin_termios(void) { tcsetattr(s_in_fd, TCSAFLUSH, &original_stdin_termios); }
60
restore_stdout_termios(void)61 static void restore_stdout_termios(void) { tcsetattr(s_out_fd, TCSAFLUSH, &original_stdout_termios); }
62
platformUartRestore(void)63 void platformUartRestore(void)
64 {
65 restore_stdin_termios();
66 restore_stdout_termios();
67 dup2(s_out_fd, STDOUT_FILENO);
68 }
69
otPlatUartEnable(void)70 otError otPlatUartEnable(void)
71 {
72 otError error = OT_ERROR_NONE;
73 struct termios termios;
74
75 s_in_fd = dup(STDIN_FILENO);
76 s_out_fd = dup(STDOUT_FILENO);
77 dup2(STDERR_FILENO, STDOUT_FILENO);
78
79 // We need this signal to make sure that this
80 // process terminates properly.
81 signal(SIGPIPE, SIG_DFL);
82
83 if (isatty(s_in_fd))
84 {
85 tcgetattr(s_in_fd, &original_stdin_termios);
86 atexit(&restore_stdin_termios);
87 }
88
89 if (isatty(s_out_fd))
90 {
91 tcgetattr(s_out_fd, &original_stdout_termios);
92 atexit(&restore_stdout_termios);
93 }
94
95 if (isatty(s_in_fd))
96 {
97 // get current configuration
98 otEXPECT_ACTION(tcgetattr(s_in_fd, &termios) == 0, perror("tcgetattr"); error = OT_ERROR_GENERIC);
99
100 // Set up the termios settings for raw mode. This turns
101 // off input/output processing, line processing, and character processing.
102 cfmakeraw(&termios);
103
104 // Set up our cflags for local use. Turn on hangup-on-close.
105 termios.c_cflag |= HUPCL | CREAD | CLOCAL;
106
107 // "Minimum number of characters for noncanonical read"
108 termios.c_cc[VMIN] = 1;
109
110 // "Timeout in deciseconds for noncanonical read"
111 termios.c_cc[VTIME] = 0;
112
113 // configure baud rate
114 otEXPECT_ACTION(cfsetispeed(&termios, OPENTHREAD_SIMULATION_UART_BAUDRATE) == 0, perror("cfsetispeed");
115 error = OT_ERROR_GENERIC);
116
117 // set configuration
118 otEXPECT_ACTION(tcsetattr(s_in_fd, TCSANOW, &termios) == 0, perror("tcsetattr"); error = OT_ERROR_GENERIC);
119 }
120
121 if (isatty(s_out_fd))
122 {
123 // get current configuration
124 otEXPECT_ACTION(tcgetattr(s_out_fd, &termios) == 0, perror("tcgetattr"); error = OT_ERROR_GENERIC);
125
126 // Set up the termios settings for raw mode. This turns
127 // off input/output processing, line processing, and character processing.
128 cfmakeraw(&termios);
129
130 // Absolutely obliterate all output processing.
131 termios.c_oflag = 0;
132
133 // Set up our cflags for local use. Turn on hangup-on-close.
134 termios.c_cflag |= HUPCL | CREAD | CLOCAL;
135
136 // configure baud rate
137 otEXPECT_ACTION(cfsetospeed(&termios, OPENTHREAD_SIMULATION_UART_BAUDRATE) == 0, perror("cfsetospeed");
138 error = OT_ERROR_GENERIC);
139
140 // set configuration
141 otEXPECT_ACTION(tcsetattr(s_out_fd, TCSANOW, &termios) == 0, perror("tcsetattr"); error = OT_ERROR_GENERIC);
142 }
143
144 return error;
145
146 exit:
147 close(s_in_fd);
148 close(s_out_fd);
149 return error;
150 }
151
otPlatUartDisable(void)152 otError otPlatUartDisable(void)
153 {
154 otError error = OT_ERROR_NONE;
155
156 close(s_in_fd);
157 close(s_out_fd);
158
159 return error;
160 }
161
otPlatUartSend(const uint8_t * aBuf,uint16_t aBufLength)162 otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength)
163 {
164 otError error = OT_ERROR_NONE;
165
166 otEXPECT_ACTION(s_write_length == 0, error = OT_ERROR_BUSY);
167
168 s_write_buffer = aBuf;
169 s_write_length = aBufLength;
170
171 exit:
172 return error;
173 }
174
platformUartUpdateFdSet(fd_set * aReadFdSet,fd_set * aWriteFdSet,fd_set * aErrorFdSet,int * aMaxFd)175 void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd)
176 {
177 utilsAddFdToFdSet(s_in_fd, aReadFdSet, aMaxFd);
178 utilsAddFdToFdSet(s_in_fd, aErrorFdSet, aMaxFd);
179
180 if ((s_write_length > 0))
181 {
182 utilsAddFdToFdSet(s_out_fd, aWriteFdSet, aMaxFd);
183 utilsAddFdToFdSet(s_out_fd, aErrorFdSet, aMaxFd);
184 }
185 }
186
otPlatUartFlush(void)187 otError otPlatUartFlush(void)
188 {
189 otError error = OT_ERROR_NONE;
190 ssize_t count;
191
192 otEXPECT_ACTION(s_write_buffer != NULL && s_write_length > 0, error = OT_ERROR_INVALID_STATE);
193
194 while ((count = write(s_out_fd, s_write_buffer, s_write_length)) > 0 && (s_write_length -= count) > 0)
195 {
196 s_write_buffer += count;
197 }
198
199 if (count != -1)
200 {
201 assert(s_write_length == 0);
202 s_write_buffer = NULL;
203 }
204 else
205 {
206 perror("write(UART)");
207 DieNow(OT_EXIT_ERROR_ERRNO);
208 }
209
210 exit:
211 return error;
212 }
213
platformUartProcess(void)214 void platformUartProcess(void)
215 {
216 ssize_t rval;
217 const int error_flags = POLLERR | POLLNVAL | POLLHUP;
218 struct pollfd pollfd[] = {
219 {s_in_fd, POLLIN | error_flags, 0},
220 {s_out_fd, POLLOUT | error_flags, 0},
221 };
222
223 errno = 0;
224
225 rval = poll(pollfd, sizeof(pollfd) / sizeof(*pollfd), 0);
226
227 if (rval < 0)
228 {
229 perror("poll");
230 DieNow(OT_EXIT_ERROR_ERRNO);
231 }
232
233 if (rval > 0)
234 {
235 if ((pollfd[0].revents & error_flags) != 0)
236 {
237 perror("s_in_fd");
238 DieNow(OT_EXIT_ERROR_ERRNO);
239 }
240
241 if ((pollfd[1].revents & error_flags) != 0)
242 {
243 perror("s_out_fd");
244 DieNow(OT_EXIT_ERROR_ERRNO);
245 }
246
247 if (pollfd[0].revents & POLLIN)
248 {
249 rval = read(s_in_fd, s_receive_buffer, sizeof(s_receive_buffer));
250
251 if (rval <= 0)
252 {
253 perror("read");
254 DieNow(OT_EXIT_ERROR_ERRNO);
255 }
256
257 otPlatUartReceived(s_receive_buffer, (uint16_t)rval);
258 }
259
260 if ((s_write_length > 0) && (pollfd[1].revents & POLLOUT))
261 {
262 rval = write(s_out_fd, s_write_buffer, s_write_length);
263
264 if (rval >= 0)
265 {
266 s_write_buffer += (uint16_t)rval;
267 s_write_length -= (uint16_t)rval;
268
269 if (s_write_length == 0)
270 {
271 otPlatUartSendDone();
272 }
273 }
274 else if (errno != EINTR)
275 {
276 perror("write");
277 DieNow(OT_EXIT_ERROR_ERRNO);
278 }
279 }
280 }
281 }
282 #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART == 0
283
284 #if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART && (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART)
285
286 static FILE *posix_logfile;
287
otPlatDebugUart_logfile(const char * filename)288 otError otPlatDebugUart_logfile(const char *filename)
289 {
290 posix_logfile = fopen(filename, "wt");
291
292 return posix_logfile ? OT_ERROR_NONE : OT_ERROR_FAILED;
293 }
294
otPlatDebugUart_putchar_raw(int c)295 void otPlatDebugUart_putchar_raw(int c)
296 {
297 FILE *fp;
298
299 /* note: log file will have a mix of cr/lf and
300 * in some/many cases duplicate cr because in
301 * some cases the log function {ie: Mbed} already
302 * includes the CR or LF... but other log functions
303 * do not include cr/lf and expect it appended
304 */
305 fp = posix_logfile;
306
307 if (fp != NULL)
308 {
309 /* log is lost ... until a file is setup */
310 fputc(c, fp);
311 /* we could "fflush" but will not */
312 }
313 }
314
otPlatDebugUart_kbhit(void)315 int otPlatDebugUart_kbhit(void)
316 {
317 /* not supported */
318 return 0;
319 }
320
otPlatDebugUart_getc(void)321 int otPlatDebugUart_getc(void)
322 {
323 /* not supported */
324 return -1;
325 }
326
327 #endif
328