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 */ 34typedef 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 */ 43typedef 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 */ 55static inline void 56jerryx_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 **/ 70static bool 71jerryx_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 */ 105static inline bool 106jerryx_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 */ 235static void 236jerryx_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 */ 255static bool 256jerryx_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 */ 291static bool 292jerryx_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 */ 329bool 330jerryx_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 */ 402bool 403jerryx_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