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