• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright JS Foundation and other contributors, http://js.foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "jerryscript-debugger-transport.h"
17 #include "jerryscript-ext/debugger.h"
18 #include "jext-common.h"
19 
20 #if (defined (JERRY_DEBUGGER) && (JERRY_DEBUGGER == 1)) && !defined _WIN32
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <termios.h>
26 #include <stdlib.h>
27 
28 /* Max size of configuration string */
29 #define CONFIG_SIZE (255)
30 
31 /**
32  * Implementation of transport over serial connection.
33  */
34 typedef struct
35 {
36   jerry_debugger_transport_header_t header; /**< transport header */
37   int fd; /**< file descriptor */
38 } jerryx_debugger_transport_serial_t;
39 
40 /**
41  * Configure parameters for a serial port.
42  */
43 typedef struct
44 {
45   char *device_id;
46   uint32_t baud_rate; /**< specify the rate at which bits are transmitted for the serial interface */
47   uint32_t data_bits; /**< specify the number of data bits to transmit over the serial interface */
48   char parity; /**< specify how you want to check parity bits in the data bits transmitted via the serial port */
49   uint32_t stop_bits; /**< specify the number of bits used to indicate the end of a byte. */
50 } jerryx_debugger_transport_serial_config_t;
51 
52 /**
53  * Correctly close a file descriptor.
54  */
55 static inline void
jerryx_debugger_serial_close_fd(int fd)56 jerryx_debugger_serial_close_fd (int fd) /**< file descriptor to close */
57 {
58   if (close (fd) != 0)
59   {
60     JERRYX_ERROR_MSG ("Error while closing the file descriptor: %d\n", errno);
61   }
62 } /* jerryx_debugger_serial_close_fd */
63 
64 /**
65  * Set a file descriptor to blocking or non-blocking mode.
66  *
67  * @return true if everything is ok
68  *         false if there was an error
69  **/
70 static bool
jerryx_debugger_serial_set_blocking(int fd,bool blocking)71 jerryx_debugger_serial_set_blocking (int fd, bool blocking)
72 {
73   /* Save the current flags */
74   int flags = fcntl (fd, F_GETFL, 0);
75   if (flags == -1)
76   {
77     JERRYX_ERROR_MSG ("Error %d during get flags from file descriptor\n", errno);
78     return false;
79   }
80 
81   if (blocking)
82   {
83     flags &= ~O_NONBLOCK;
84   }
85   else
86   {
87     flags |= O_NONBLOCK;
88   }
89 
90   if (fcntl (fd, F_SETFL, flags) == -1)
91   {
92     JERRYX_ERROR_MSG ("Error %d during set flags from file descriptor\n", errno);
93     return false;
94   }
95 
96   return true;
97 } /* jerryx_debugger_serial_set_blocking */
98 
99 /**
100  * Configure the file descriptor used by the serial communcation.
101  *
102  * @return true if everything is ok
103  *         false if there was an error
104  */
105 static inline bool
jerryx_debugger_serial_configure_attributes(int fd,jerryx_debugger_transport_serial_config_t serial_config)106 jerryx_debugger_serial_configure_attributes (int fd, jerryx_debugger_transport_serial_config_t serial_config)
107 {
108   struct termios options;
109   memset (&options, 0, sizeof (options));
110 
111   /* Get the parameters associated with the file descriptor */
112   if (tcgetattr (fd, &options) != 0)
113   {
114     JERRYX_ERROR_MSG ("Error %d from tggetattr\n", errno);
115     return false;
116   }
117 
118   /* Set the input and output baud rates */
119   cfsetispeed (&options, serial_config.baud_rate);
120   cfsetospeed (&options, serial_config.baud_rate);
121 
122   /* Set the control modes */
123   options.c_cflag &= (uint32_t) ~CSIZE; // character size mask
124   options.c_cflag |= (CLOCAL | CREAD); // ignore modem control lines and enable the receiver
125 
126   switch (serial_config.data_bits)
127   {
128     case 5:
129     {
130       options.c_cflag |= CS5; // set character size mask to 5-bit chars
131       break;
132     }
133     case 6:
134     {
135       options.c_cflag |= CS6; // set character size mask to 6-bit chars
136       break;
137     }
138     case 7:
139     {
140       options.c_cflag |= CS7; // set character size mask to 7-bit chars
141       break;
142     }
143     case 8:
144     {
145       options.c_cflag |= CS8; // set character size mask to 8-bit chars
146       break;
147     }
148     default:
149     {
150       JERRYX_ERROR_MSG ("Unsupported data bits: %d\n", serial_config.data_bits);
151       return false;
152     }
153   }
154 
155   switch (serial_config.parity)
156   {
157     case 'N':
158     {
159       options.c_cflag &= (unsigned int) ~(PARENB | PARODD);
160       break;
161     }
162     case 'O':
163     {
164       options.c_cflag |= PARENB;
165       options.c_cflag |= PARODD;
166       break;
167     }
168     case 'E':
169     {
170       options.c_cflag |= PARENB;
171       options.c_cflag |= PARODD;
172       break;
173     }
174     default:
175     {
176       JERRYX_ERROR_MSG ("Unsupported parity: %c\n", serial_config.parity);
177       return false;
178     }
179   }
180 
181   switch (serial_config.stop_bits)
182   {
183     case 1:
184     {
185       options.c_cflag &= (uint32_t) ~CSTOPB; // set 1 stop bits
186       break;
187     }
188     case 2:
189     {
190       options.c_cflag |= CSTOPB; // set 2 stop bits
191       break;
192     }
193     default:
194     {
195       JERRYX_ERROR_MSG ("Unsupported stop bits: %d\n", serial_config.stop_bits);
196       return false;
197     }
198   }
199 
200   /* Set the input modes */
201   options.c_iflag &= (uint32_t) ~IGNBRK; // disable break processing
202   options.c_iflag &= (uint32_t) ~(IXON | IXOFF | IXANY); // disable xon/xoff ctrl
203 
204   /* Set the output modes: no remapping, no delays */
205   options.c_oflag = 0;
206 
207   /* Set the local modes: no signaling chars, no echo, no canoncial processing */
208   options.c_lflag = 0;
209 
210   /* Read returns when at least one byte of data is available. */
211   options.c_cc[VMIN]  = 1; // read block
212   options.c_cc[VTIME] = 5; // 0.5 seconds read timeout
213 
214   /* Set the parameters associated with the file descriptor */
215   if (tcsetattr (fd, TCSANOW, &options) != 0)
216   {
217     JERRYX_ERROR_MSG ("Error %d from tcsetattr", errno);
218     return false;
219   }
220 
221   /* Flushes both data received but not read, and data written but not transmitted */
222   if (tcflush (fd, TCIOFLUSH) != 0)
223   {
224     JERRYX_ERROR_MSG ("Error %d in tcflush() :%s\n", errno, strerror (errno));
225     jerryx_debugger_serial_close_fd (fd);
226     return false;
227   }
228 
229   return true;
230 } /* jerryx_debugger_serial_configure_attributes */
231 
232 /**
233  * Close a serial connection.
234  */
235 static void
jerryx_debugger_serial_close(jerry_debugger_transport_header_t * header_p)236 jerryx_debugger_serial_close (jerry_debugger_transport_header_t *header_p) /**< serial implementation */
237 {
238   JERRYX_ASSERT (!jerry_debugger_transport_is_connected ());
239 
240   jerryx_debugger_transport_serial_t *serial_p = (jerryx_debugger_transport_serial_t *) header_p;
241 
242   JERRYX_DEBUG_MSG ("Serial connection closed.\n");
243 
244   jerryx_debugger_serial_close_fd (serial_p->fd);
245 
246   jerry_heap_free ((void *) header_p, sizeof (jerryx_debugger_transport_serial_t));
247 } /* jerryx_debugger_serial_close */
248 
249 /**
250  * Send data over a serial connection.
251  *
252  * @return true - if the data has been sent successfully
253  *         false - otherwise
254  */
255 static bool
jerryx_debugger_serial_send(jerry_debugger_transport_header_t * header_p,uint8_t * message_p,size_t message_length)256 jerryx_debugger_serial_send (jerry_debugger_transport_header_t *header_p, /**< serial implementation */
257                              uint8_t *message_p, /**< message to be sent */
258                              size_t message_length) /**< message length in bytes */
259 {
260   JERRYX_ASSERT (jerry_debugger_transport_is_connected ());
261 
262   jerryx_debugger_transport_serial_t *serial_p = (jerryx_debugger_transport_serial_t *) header_p;
263 
264   do
265   {
266     ssize_t sent_bytes = write (serial_p->fd, message_p, message_length);
267 
268     if (sent_bytes < 0)
269     {
270       if (errno == EWOULDBLOCK)
271       {
272         continue;
273       }
274 
275       JERRYX_ERROR_MSG ("Error: write to file descriptor: %d\n", errno);
276       jerry_debugger_transport_close ();
277       return false;
278     }
279 
280     message_p += sent_bytes;
281     message_length -= (size_t) sent_bytes;
282   }
283   while (message_length > 0);
284 
285   return true;
286 } /* jerryx_debugger_serial_send */
287 
288 /**
289  * Receive data from a serial connection.
290  */
291 static bool
jerryx_debugger_serial_receive(jerry_debugger_transport_header_t * header_p,jerry_debugger_transport_receive_context_t * receive_context_p)292 jerryx_debugger_serial_receive (jerry_debugger_transport_header_t *header_p, /**< serial implementation */
293                                 jerry_debugger_transport_receive_context_t *receive_context_p) /**< receive context */
294 {
295   jerryx_debugger_transport_serial_t *serial_p = (jerryx_debugger_transport_serial_t *) header_p;
296 
297   uint8_t *buffer_p = receive_context_p->buffer_p + receive_context_p->received_length;
298   size_t buffer_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE - receive_context_p->received_length;
299 
300   ssize_t length = read (serial_p->fd, buffer_p, buffer_size);
301 
302   if (length <= 0)
303   {
304     if (errno != EWOULDBLOCK || length == 0)
305     {
306       jerry_debugger_transport_close ();
307       return false;
308     }
309     length = 0;
310   }
311 
312   receive_context_p->received_length += (size_t) length;
313 
314   if (receive_context_p->received_length > 0)
315   {
316     receive_context_p->message_p = receive_context_p->buffer_p;
317     receive_context_p->message_length = receive_context_p->received_length;
318   }
319 
320   return true;
321 } /* jerryx_debugger_serial_receive */
322 
323 /**
324  * Create a serial connection.
325  *
326  * @return true if successful,
327  *         false otherwise
328  */
329 bool
jerryx_debugger_serial_create(const char * config)330 jerryx_debugger_serial_create (const char *config) /**< specify the configuration */
331 {
332   /* Parse the configuration string */
333   char tmp_config[CONFIG_SIZE];
334   strncpy (tmp_config, config, CONFIG_SIZE);
335   jerryx_debugger_transport_serial_config_t serial_config;
336 
337   char *token = strtok (tmp_config, ",");
338   serial_config.device_id = token ? token : "/dev/ttyS0";
339   serial_config.baud_rate = (token = strtok (NULL, ",")) ? (uint32_t) strtoul (token, NULL, 10) : 115200;
340   serial_config.data_bits = (token = strtok (NULL, ",")) ? (uint32_t) strtoul (token, NULL, 10) : 8;
341   serial_config.parity = (token = strtok (NULL, ",")) ? token[0] : 'N';
342   serial_config.stop_bits = (token = strtok (NULL, ",")) ? (uint32_t) strtoul (token, NULL, 10) : 1;
343 
344   int fd = open (serial_config.device_id, O_RDWR);
345 
346   if (fd < 0)
347   {
348     JERRYX_ERROR_MSG ("Error %d opening %s: %s", errno, serial_config.device_id, strerror (errno));
349     return false;
350   }
351 
352   if (!jerryx_debugger_serial_configure_attributes (fd, serial_config))
353   {
354     jerryx_debugger_serial_close_fd (fd);
355     return false;
356   }
357 
358   JERRYX_DEBUG_MSG ("Waiting for client connection\n");
359 
360   /* Client will sent a 'c' char to initiate the connection. */
361   uint8_t conn_char;
362   ssize_t t = read (fd, &conn_char, 1);
363   if (t != 1 || conn_char != 'c' || !jerryx_debugger_serial_set_blocking (fd, false))
364   {
365     return false;
366   }
367 
368   JERRYX_DEBUG_MSG ("Client connected\n");
369 
370   size_t size = sizeof (jerryx_debugger_transport_serial_t);
371 
372   jerry_debugger_transport_header_t *header_p;
373   header_p = (jerry_debugger_transport_header_t *) jerry_heap_alloc (size);
374 
375   if (!header_p)
376   {
377     jerryx_debugger_serial_close_fd (fd);
378     return false;
379   }
380 
381   header_p->close = jerryx_debugger_serial_close;
382   header_p->send = jerryx_debugger_serial_send;
383   header_p->receive = jerryx_debugger_serial_receive;
384 
385   ((jerryx_debugger_transport_serial_t *) header_p)->fd = fd;
386 
387   jerry_debugger_transport_add (header_p,
388                                 0,
389                                 JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE,
390                                 0,
391                                 JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE);
392 
393   return true;
394 } /* jerryx_debugger_serial_create */
395 
396 #else /* !(defined (JERRY_DEBUGGER) && (JERRY_DEBUGGER == 1)) || _WIN32 */
397 /**
398  * Dummy function when debugger is disabled.
399  *
400  * @return false
401  */
402 bool
jerryx_debugger_serial_create(const char * config)403 jerryx_debugger_serial_create (const char *config)
404 {
405   JERRYX_UNUSED (config);
406   return false;
407 } /* jerryx_debugger_serial_create */
408 
409 #endif /* (defined (JERRY_DEBUGGER) && (JERRY_DEBUGGER == 1)) && !defined _WIN32 */
410