|
23 | 23 | #include "sdkconfig.h" |
24 | 24 | #include "soc/soc_caps.h" |
25 | 25 | #include "hal/usb_serial_jtag_ll.h" |
| 26 | +#include "driver/usb_serial_jtag_select.h" |
26 | 27 | #include "driver/usb_serial_jtag_vfs.h" |
27 | 28 | #include "driver/usb_serial_jtag.h" |
28 | 29 | #include "esp_private/startup_internal.h" |
| 30 | +#include "esp_heap_caps.h" |
| 31 | + |
| 32 | +// local file descriptor value for the USJ |
| 33 | +#define USJ_LOCAL_FD 0 |
29 | 34 |
|
30 | 35 | // Token signifying that no character is available |
31 | 36 | #define NONE -1 |
|
46 | 51 | # define DEFAULT_RX_MODE ESP_LINE_ENDINGS_LF |
47 | 52 | #endif |
48 | 53 |
|
| 54 | +#if CONFIG_VFS_SELECT_IN_RAM |
| 55 | +#define USJ_VFS_MALLOC_FLAGS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) |
| 56 | +#else |
| 57 | +#define USJ_VFS_MALLOC_FLAGS MALLOC_CAP_DEFAULT |
| 58 | +#endif |
| 59 | + |
49 | 60 | // write bytes function type |
50 | 61 | typedef void (*tx_func_t)(int, int); |
51 | 62 | // read bytes function type |
@@ -108,10 +119,30 @@ static usb_serial_jtag_vfs_context_t s_ctx = { |
108 | 119 | .fsync_func = usb_serial_jtag_wait_tx_done_no_driver |
109 | 120 | }; |
110 | 121 |
|
| 122 | +#ifdef CONFIG_VFS_SUPPORT_SELECT |
| 123 | + |
| 124 | +typedef struct { |
| 125 | + esp_vfs_select_sem_t select_sem; |
| 126 | + fd_set *readfds; |
| 127 | + fd_set *writefds; |
| 128 | + fd_set *errorfds; |
| 129 | + fd_set readfds_orig; |
| 130 | + fd_set writefds_orig; |
| 131 | + fd_set errorfds_orig; |
| 132 | +} usb_serial_jtag_select_args_t; |
| 133 | + |
| 134 | +static usb_serial_jtag_select_args_t **s_registered_selects = NULL; |
| 135 | +static int s_registered_select_num = 0; |
| 136 | +static portMUX_TYPE s_registered_select_lock = portMUX_INITIALIZER_UNLOCKED; |
| 137 | + |
| 138 | +static esp_err_t usb_serial_jtag_end_select(void *end_select_args); |
| 139 | + |
| 140 | +#endif // CONFIG_VFS_SUPPORT_SELECT |
| 141 | + |
111 | 142 | static int usb_serial_jtag_open(const char * path, int flags, int mode) |
112 | 143 | { |
113 | 144 | s_ctx.non_blocking = ((flags & O_NONBLOCK) == O_NONBLOCK); |
114 | | - return 0; |
| 145 | + return USJ_LOCAL_FD; |
115 | 146 | } |
116 | 147 |
|
117 | 148 | static void usb_serial_jtag_tx_char_no_driver(int fd, int c) |
@@ -300,6 +331,164 @@ static int usb_serial_jtag_fsync(int fd) |
300 | 331 | } |
301 | 332 | } |
302 | 333 |
|
| 334 | +#ifdef CONFIG_VFS_SUPPORT_SELECT |
| 335 | + |
| 336 | +static void select_notif_callback_isr(usj_select_notif_t usj_select_notif, BaseType_t *task_woken) |
| 337 | +{ |
| 338 | + portENTER_CRITICAL_ISR(&s_registered_select_lock); |
| 339 | + for (int i = 0; i < s_registered_select_num; ++i) { |
| 340 | + usb_serial_jtag_select_args_t *args = s_registered_selects[i]; |
| 341 | + if (args) { |
| 342 | + switch (usj_select_notif) { |
| 343 | + case USJ_SELECT_READ_NOTIF: |
| 344 | + if (FD_ISSET(USJ_LOCAL_FD, &args->readfds_orig)) { |
| 345 | + FD_SET(USJ_LOCAL_FD, args->readfds); |
| 346 | + esp_vfs_select_triggered_isr(args->select_sem, task_woken); |
| 347 | + } |
| 348 | + break; |
| 349 | + case USJ_SELECT_WRITE_NOTIF: |
| 350 | + if (FD_ISSET(USJ_LOCAL_FD, &args->writefds_orig)) { |
| 351 | + FD_SET(USJ_LOCAL_FD, args->writefds); |
| 352 | + esp_vfs_select_triggered_isr(args->select_sem, task_woken); |
| 353 | + } |
| 354 | + break; |
| 355 | + case USJ_SELECT_ERROR_NOTIF: |
| 356 | + if (FD_ISSET(USJ_LOCAL_FD, &args->errorfds_orig)) { |
| 357 | + FD_SET(USJ_LOCAL_FD, args->errorfds); |
| 358 | + esp_vfs_select_triggered_isr(args->select_sem, task_woken); |
| 359 | + } |
| 360 | + break; |
| 361 | + } |
| 362 | + } |
| 363 | + } |
| 364 | + portEXIT_CRITICAL_ISR(&s_registered_select_lock); |
| 365 | +} |
| 366 | + |
| 367 | +static esp_err_t register_select(usb_serial_jtag_select_args_t *args) |
| 368 | +{ |
| 369 | + esp_err_t ret = ESP_ERR_INVALID_ARG; |
| 370 | + |
| 371 | + if (args) { |
| 372 | + portENTER_CRITICAL(&s_registered_select_lock); |
| 373 | + const int new_size = s_registered_select_num + 1; |
| 374 | + usb_serial_jtag_select_args_t **new_selects; |
| 375 | + if ((new_selects = heap_caps_realloc(s_registered_selects, new_size * sizeof(usb_serial_jtag_select_args_t *), USJ_VFS_MALLOC_FLAGS)) == NULL) { |
| 376 | + ret = ESP_ERR_NO_MEM; |
| 377 | + } else { |
| 378 | + /* on first select registration register the callback */ |
| 379 | + if (s_registered_select_num == 0) { |
| 380 | + usb_serial_jtag_set_select_notif_callback(select_notif_callback_isr); |
| 381 | + } |
| 382 | + |
| 383 | + s_registered_selects = new_selects; |
| 384 | + s_registered_selects[s_registered_select_num] = args; |
| 385 | + s_registered_select_num = new_size; |
| 386 | + ret = ESP_OK; |
| 387 | + } |
| 388 | + portEXIT_CRITICAL(&s_registered_select_lock); |
| 389 | + } |
| 390 | + |
| 391 | + return ret; |
| 392 | +} |
| 393 | + |
| 394 | +static esp_err_t unregister_select(usb_serial_jtag_select_args_t *args) |
| 395 | +{ |
| 396 | + esp_err_t ret = ESP_OK; |
| 397 | + if (args) { |
| 398 | + ret = ESP_ERR_INVALID_STATE; |
| 399 | + portENTER_CRITICAL(&s_registered_select_lock); |
| 400 | + for (int i = 0; i < s_registered_select_num; ++i) { |
| 401 | + if (s_registered_selects[i] == args) { |
| 402 | + const int new_size = s_registered_select_num - 1; |
| 403 | + // The item is removed by overwriting it with the last item. The subsequent rellocation will drop the |
| 404 | + // last item. |
| 405 | + s_registered_selects[i] = s_registered_selects[new_size]; |
| 406 | + s_registered_selects = heap_caps_realloc(s_registered_selects, new_size * sizeof(usb_serial_jtag_select_args_t *), USJ_VFS_MALLOC_FLAGS); |
| 407 | + // Shrinking a buffer with realloc is guaranteed to succeed. |
| 408 | + s_registered_select_num = new_size; |
| 409 | + |
| 410 | + /* when the last select is unregistered, also unregister the callback */ |
| 411 | + if (s_registered_select_num == 0) { |
| 412 | + usb_serial_jtag_set_select_notif_callback(NULL); |
| 413 | + } |
| 414 | + |
| 415 | + ret = ESP_OK; |
| 416 | + break; |
| 417 | + } |
| 418 | + } |
| 419 | + portEXIT_CRITICAL(&s_registered_select_lock); |
| 420 | + } |
| 421 | + return ret; |
| 422 | +} |
| 423 | + |
| 424 | +static esp_err_t usb_serial_jtag_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, |
| 425 | + esp_vfs_select_sem_t select_sem, void **end_select_args) |
| 426 | +{ |
| 427 | + (void)nfds; /* Since there is only 1 usb serial jtag port, this parameter is useless */ |
| 428 | + *end_select_args = NULL; |
| 429 | + if (!usb_serial_jtag_is_driver_installed()) { |
| 430 | + return ESP_ERR_INVALID_STATE; |
| 431 | + } |
| 432 | + |
| 433 | + usb_serial_jtag_select_args_t *args = heap_caps_malloc(sizeof(usb_serial_jtag_select_args_t), USJ_VFS_MALLOC_FLAGS); |
| 434 | + |
| 435 | + if (args == NULL) { |
| 436 | + return ESP_ERR_NO_MEM; |
| 437 | + } |
| 438 | + args->select_sem = select_sem; |
| 439 | + args->readfds = readfds; |
| 440 | + args->writefds = writefds; |
| 441 | + args->errorfds = exceptfds; |
| 442 | + args->readfds_orig = *readfds; // store the original values because they will be set to zero |
| 443 | + args->writefds_orig = *writefds; |
| 444 | + args->errorfds_orig = *exceptfds; |
| 445 | + FD_ZERO(readfds); |
| 446 | + FD_ZERO(writefds); |
| 447 | + FD_ZERO(exceptfds); |
| 448 | + |
| 449 | + esp_err_t ret = register_select(args); |
| 450 | + if (ret != ESP_OK) { |
| 451 | + free(args); |
| 452 | + return ret; |
| 453 | + } |
| 454 | + |
| 455 | + bool trigger_select = false; |
| 456 | + |
| 457 | + // check if the select should return instantly if the bus is read ready |
| 458 | + if (FD_ISSET(USJ_LOCAL_FD, &args->readfds_orig) && usb_serial_jtag_read_ready()) { |
| 459 | + // signal immediately when data is buffered |
| 460 | + FD_SET(USJ_LOCAL_FD, readfds); |
| 461 | + trigger_select = true; |
| 462 | + } |
| 463 | + |
| 464 | + // check if the select should return instantly if the bus is write ready |
| 465 | + if (FD_ISSET(USJ_LOCAL_FD, &args->writefds_orig) && usb_serial_jtag_write_ready()) { |
| 466 | + // signal immediately when data can be written |
| 467 | + FD_SET(USJ_LOCAL_FD, writefds); |
| 468 | + trigger_select = true; |
| 469 | + } |
| 470 | + |
| 471 | + if (trigger_select) { |
| 472 | + esp_vfs_select_triggered(args->select_sem); |
| 473 | + } |
| 474 | + |
| 475 | + *end_select_args = args; |
| 476 | + return ESP_OK; |
| 477 | +} |
| 478 | + |
| 479 | +static esp_err_t usb_serial_jtag_end_select(void *end_select_args) |
| 480 | +{ |
| 481 | + usb_serial_jtag_select_args_t *args = end_select_args; |
| 482 | + esp_err_t ret = unregister_select(args); |
| 483 | + if (args) { |
| 484 | + free(args); |
| 485 | + } |
| 486 | + |
| 487 | + return ret; |
| 488 | +} |
| 489 | + |
| 490 | +#endif // CONFIG_VFS_SUPPORT_SELECT |
| 491 | + |
303 | 492 | #ifdef CONFIG_VFS_SUPPORT_TERMIOS |
304 | 493 | static int usb_serial_jtag_tcsetattr(int fd, int optional_actions, const struct termios *p) |
305 | 494 | { |
@@ -391,6 +580,10 @@ static const esp_vfs_t usj_vfs = { |
391 | 580 | .read = &usb_serial_jtag_read, |
392 | 581 | .fcntl = &usb_serial_jtag_fcntl, |
393 | 582 | .fsync = &usb_serial_jtag_fsync, |
| 583 | +#ifdef CONFIG_VFS_SUPPORT_SELECT |
| 584 | + .start_select = &usb_serial_jtag_start_select, |
| 585 | + .end_select = &usb_serial_jtag_end_select, |
| 586 | +#endif // CONFIG_VFS_SUPPORT_SELECT |
394 | 587 | #ifdef CONFIG_VFS_SUPPORT_TERMIOS |
395 | 588 | .tcsetattr = &usb_serial_jtag_tcsetattr, |
396 | 589 | .tcgetattr = &usb_serial_jtag_tcgetattr, |
|
0 commit comments