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