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