forked from espressif/esp-idf
Merge branch 'feat/usb-cdc-select' into 'master'
feat(usb_cdc): Add select functionality to the driver Closes IDF-10281 See merge request espressif/esp-idf!31686
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CDCACM_SELECT_READ_NOTIF,
|
||||||
|
CDCACM_SELECT_WRITE_NOTIF,
|
||||||
|
CDCACM_SELECT_ERROR_NOTIF,
|
||||||
|
} cdcacm_select_notif_t;
|
||||||
|
|
||||||
|
typedef void (*cdcacm_select_notif_callback_t)(cdcacm_select_notif_t cdcacm_select_notif, BaseType_t *task_woken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set notification callback function for select() events
|
||||||
|
* @param cdcacm_select_notif_callback callback function
|
||||||
|
*/
|
||||||
|
void cdcacm_set_select_notif_callback(cdcacm_select_notif_callback_t cdcacm_select_notif_callback);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -91,6 +92,15 @@ bool esp_usb_console_write_available(void);
|
|||||||
*/
|
*/
|
||||||
esp_err_t esp_usb_console_set_cb(esp_usb_console_cb_t rx_cb, esp_usb_console_cb_t tx_cb, void* arg);
|
esp_err_t esp_usb_console_set_cb(esp_usb_console_cb_t rx_cb, esp_usb_console_cb_t tx_cb, void* arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether the USB console is installed or not
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - true USB console is installed
|
||||||
|
* - false USB console is not installed
|
||||||
|
*/
|
||||||
|
bool esp_usb_console_is_installed(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -46,6 +46,8 @@
|
|||||||
#include "esp32s3/rom/usb/chip_usb_dw_wrapper.h"
|
#include "esp32s3/rom/usb/chip_usb_dw_wrapper.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "esp_private/esp_vfs_cdcacm_select.h"
|
||||||
|
|
||||||
#define CDC_WORK_BUF_SIZE (ESP_ROM_CDC_ACM_WORK_BUF_MIN + CONFIG_ESP_CONSOLE_USB_CDC_RX_BUF_SIZE)
|
#define CDC_WORK_BUF_SIZE (ESP_ROM_CDC_ACM_WORK_BUF_MIN + CONFIG_ESP_CONSOLE_USB_CDC_RX_BUF_SIZE)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -55,6 +57,7 @@ typedef enum {
|
|||||||
REBOOT_BOOTLOADER_DFU,
|
REBOOT_BOOTLOADER_DFU,
|
||||||
} reboot_type_t;
|
} reboot_type_t;
|
||||||
|
|
||||||
|
static bool s_usb_installed = false;
|
||||||
static reboot_type_t s_queue_reboot = REBOOT_NONE;
|
static reboot_type_t s_queue_reboot = REBOOT_NONE;
|
||||||
static int s_prev_rts_state;
|
static int s_prev_rts_state;
|
||||||
static intr_handle_t s_usb_int_handle;
|
static intr_handle_t s_usb_int_handle;
|
||||||
@@ -131,6 +134,18 @@ int esp_usb_console_osglue_wait_proc(int delay_us)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* USB interrupt handler, forward the call to the ROM driver.
|
||||||
|
* Non-static to allow placement into IRAM by ldgen.
|
||||||
|
*/
|
||||||
|
static cdcacm_select_notif_callback_t s_cdcacm_select_notif_callback = NULL;
|
||||||
|
|
||||||
|
void cdcacm_set_select_notif_callback(cdcacm_select_notif_callback_t cdcacm_select_notif_callback)
|
||||||
|
{
|
||||||
|
if (esp_usb_console_is_installed()) {
|
||||||
|
s_cdcacm_select_notif_callback = cdcacm_select_notif_callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Called by ROM CDC ACM driver from interrupt context./
|
/* Called by ROM CDC ACM driver from interrupt context./
|
||||||
* Non-static to allow placement into IRAM by ldgen.
|
* Non-static to allow placement into IRAM by ldgen.
|
||||||
*/
|
*/
|
||||||
@@ -168,6 +183,8 @@ void esp_usb_console_dfu_detach_cb(int timeout)
|
|||||||
*/
|
*/
|
||||||
void esp_usb_console_interrupt(void *arg)
|
void esp_usb_console_interrupt(void *arg)
|
||||||
{
|
{
|
||||||
|
BaseType_t xTaskWoken = 0;
|
||||||
|
|
||||||
usb_dc_check_poll_for_interrupts();
|
usb_dc_check_poll_for_interrupts();
|
||||||
/* Restart can be requested from esp_usb_console_cdc_acm_cb or esp_usb_console_dfu_detach_cb */
|
/* Restart can be requested from esp_usb_console_cdc_acm_cb or esp_usb_console_dfu_detach_cb */
|
||||||
if (s_queue_reboot != REBOOT_NONE) {
|
if (s_queue_reboot != REBOOT_NONE) {
|
||||||
@@ -192,6 +209,21 @@ void esp_usb_console_interrupt(void *arg)
|
|||||||
esp_restart_noos();
|
esp_restart_noos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (esp_usb_console_available_for_read() > 0) {
|
||||||
|
if (s_cdcacm_select_notif_callback != NULL) {
|
||||||
|
s_cdcacm_select_notif_callback(CDCACM_SELECT_READ_NOTIF, &xTaskWoken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (esp_usb_console_write_available()) {
|
||||||
|
if (s_cdcacm_select_notif_callback != NULL) {
|
||||||
|
s_cdcacm_select_notif_callback(CDCACM_SELECT_WRITE_NOTIF, &xTaskWoken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xTaskWoken == pdTRUE) {
|
||||||
|
portYIELD_FROM_ISR();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called as esp_timer callback when the restart timeout expires.
|
/* Called as esp_timer callback when the restart timeout expires.
|
||||||
@@ -299,6 +331,7 @@ esp_err_t esp_usb_console_init(void)
|
|||||||
esp_rom_install_channel_putc(1, &esp_usb_console_write_char);
|
esp_rom_install_channel_putc(1, &esp_usb_console_write_char);
|
||||||
#endif // CONFIG_ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF
|
#endif // CONFIG_ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF
|
||||||
|
|
||||||
|
s_usb_installed = true;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,6 +455,11 @@ esp_err_t esp_usb_console_set_cb(esp_usb_console_cb_t rx_cb, esp_usb_console_cb_
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool esp_usb_console_is_installed(void)
|
||||||
|
{
|
||||||
|
return s_usb_installed;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t esp_usb_console_available_for_read(void)
|
ssize_t esp_usb_console_available_for_read(void)
|
||||||
{
|
{
|
||||||
if (s_cdc_acm_device == NULL) {
|
if (s_cdc_acm_device == NULL) {
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||||
|
components/esp_vfs_console/test_apps/usb_cdc_vfs:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32s3"] # reason: console components is only implemented on these targets. TODO P4: IDF-9120
|
@@ -0,0 +1,9 @@
|
|||||||
|
# This is the project CMakeLists.txt file for the test subproject
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
list(PREPEND SDKCONFIG_DEFAULTS "$ENV{IDF_PATH}/tools/test_apps/configs/sdkconfig.debug_helpers" "sdkconfig.defaults")
|
||||||
|
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(usb_cdc_vfs_test)
|
@@ -0,0 +1,2 @@
|
|||||||
|
| Supported Targets | ESP32-S3 |
|
||||||
|
| ----------------- | -------- |
|
@@ -0,0 +1,7 @@
|
|||||||
|
set(src "test_app_main.c")
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${src}
|
||||||
|
PRIV_INCLUDE_DIRS .
|
||||||
|
PRIV_REQUIRES esp_system esp_vfs_console unity
|
||||||
|
WHOLE_ARCHIVE
|
||||||
|
)
|
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "esp_private/usb_console.h"
|
||||||
|
#include "esp_vfs_cdcacm.h"
|
||||||
|
|
||||||
|
static int read_bytes_with_select(FILE *stream, void *buf, size_t buf_len, struct timeval tv)
|
||||||
|
{
|
||||||
|
int fd = fileno(stream);
|
||||||
|
fd_set read_fds;
|
||||||
|
FD_ZERO(&read_fds);
|
||||||
|
FD_SET(fd, &read_fds);
|
||||||
|
|
||||||
|
/* call select with to wait for either a read ready or timeout to happen */
|
||||||
|
int nread = select(fd + 1, &read_fds, NULL, NULL, &tv);
|
||||||
|
if (nread < 0) {
|
||||||
|
return -1;
|
||||||
|
} else if (FD_ISSET(fd, &read_fds)) {
|
||||||
|
int read_count = 0;
|
||||||
|
int total_read = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
read_count = read(fileno(stream), buf + total_read, 1);
|
||||||
|
if (read_count < 0 && errno != EWOULDBLOCK) {
|
||||||
|
return -1;
|
||||||
|
} else if (read_count > 0) {
|
||||||
|
total_read += read_count;
|
||||||
|
if (total_read > buf_len) {
|
||||||
|
fflush(stream);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (read_count > 0);
|
||||||
|
|
||||||
|
return total_read;
|
||||||
|
} else {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = 1;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
char out_buffer[32];
|
||||||
|
memset(out_buffer, 0, sizeof(out_buffer));
|
||||||
|
size_t out_buffer_len = sizeof(out_buffer);
|
||||||
|
|
||||||
|
// stdin needs to be non blocking to properly call read after select returns
|
||||||
|
// with read ready on stdin.
|
||||||
|
int fd = fileno(stdin);
|
||||||
|
int flags = fcntl(fd, F_GETFL);
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
int res = fcntl(fd, F_SETFL, flags);
|
||||||
|
TEST_ASSERT(res == 0);
|
||||||
|
|
||||||
|
// init driver
|
||||||
|
// ESP_ERROR_CHECK(esp_usb_console_init());
|
||||||
|
// esp_vfs_dev_cdcacm_register();
|
||||||
|
|
||||||
|
// send the message from pytest environment and make sure it can be read
|
||||||
|
bool message_received = false;
|
||||||
|
size_t char_read = 0;
|
||||||
|
while (!message_received && out_buffer_len > char_read) {
|
||||||
|
int nread = read_bytes_with_select(stdin, out_buffer + char_read, out_buffer_len - char_read, tv);
|
||||||
|
if (nread > 0) {
|
||||||
|
char_read += nread;
|
||||||
|
if (out_buffer[char_read - 1] == '\n') {
|
||||||
|
message_received = true;
|
||||||
|
}
|
||||||
|
} else if (nread == -2) {
|
||||||
|
// time out occurred, send the expected message back to the testing
|
||||||
|
// environment to trigger the testing environment into sending the
|
||||||
|
// test message. don't update this message without updating the pytest
|
||||||
|
// function since the string is expected as is by the test environment
|
||||||
|
char timeout_msg[] = "select timed out\n";
|
||||||
|
write(fileno(stdout), timeout_msg, sizeof(timeout_msg));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the received message back to the test environment. The test
|
||||||
|
// environment will check that the message received matches the one sent
|
||||||
|
write(fileno(stdout), out_buffer, char_read);
|
||||||
|
|
||||||
|
vTaskDelay(10); // wait for the string to send
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.esp32s3
|
||||||
|
@pytest.mark.usb_device
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'port, flash_port, config',
|
||||||
|
[
|
||||||
|
pytest.param('/dev/serial_ports/ttyACM-esp32', '/dev/serial_ports/ttyUSB-esp32', 'release'),
|
||||||
|
],
|
||||||
|
indirect=True,)
|
||||||
|
@pytest.mark.parametrize('test_message', ['test123456789!@#%^&*'])
|
||||||
|
def test_usb_cdc_vfs_default(dut: Dut, test_message: str) -> None:
|
||||||
|
dut.expect_exact('select timed out', timeout=2)
|
||||||
|
dut.write(test_message)
|
||||||
|
dut.expect_exact(test_message, timeout=2)
|
@@ -0,0 +1,6 @@
|
|||||||
|
CONFIG_PM_ENABLE=y
|
||||||
|
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||||
|
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
@@ -0,0 +1,8 @@
|
|||||||
|
# Enable Unity fixture support
|
||||||
|
CONFIG_UNITY_ENABLE_FIXTURE=n
|
||||||
|
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
|
||||||
|
|
||||||
|
# Custom partition table for this test app
|
||||||
|
CONFIG_ESP_TASK_WDT_INIT=n
|
||||||
|
|
||||||
|
CONFIG_ESP_CONSOLE_USB_CDC=y
|
@@ -15,9 +15,13 @@
|
|||||||
#include "esp_vfs_cdcacm.h"
|
#include "esp_vfs_cdcacm.h"
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
|
||||||
|
#include "esp_private/esp_vfs_cdcacm_select.h"
|
||||||
#include "esp_private/usb_console.h"
|
#include "esp_private/usb_console.h"
|
||||||
|
|
||||||
|
#define USB_CDC_LOCAL_FD 0
|
||||||
|
|
||||||
// Newline conversion mode when transmitting
|
// Newline conversion mode when transmitting
|
||||||
static esp_line_endings_t s_tx_mode =
|
static esp_line_endings_t s_tx_mode =
|
||||||
#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
|
#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
|
||||||
@@ -38,6 +42,12 @@ static esp_line_endings_t s_rx_mode =
|
|||||||
ESP_LINE_ENDINGS_LF;
|
ESP_LINE_ENDINGS_LF;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_VFS_SELECT_IN_RAM
|
||||||
|
#define CDCACM_VFS_MALLOC_FLAGS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||||
|
#else
|
||||||
|
#define CDCACM_VFS_MALLOC_FLAGS MALLOC_CAP_DEFAULT
|
||||||
|
#endif
|
||||||
|
|
||||||
#define NONE -1
|
#define NONE -1
|
||||||
|
|
||||||
//Read and write lock, lazily initialized
|
//Read and write lock, lazily initialized
|
||||||
@@ -48,6 +58,26 @@ static bool s_blocking;
|
|||||||
static SemaphoreHandle_t s_rx_semaphore;
|
static SemaphoreHandle_t s_rx_semaphore;
|
||||||
static SemaphoreHandle_t s_tx_semaphore;
|
static SemaphoreHandle_t s_tx_semaphore;
|
||||||
|
|
||||||
|
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_vfs_select_sem_t select_sem;
|
||||||
|
fd_set *readfds;
|
||||||
|
fd_set *writefds;
|
||||||
|
fd_set *errorfds;
|
||||||
|
fd_set readfds_orig;
|
||||||
|
fd_set writefds_orig;
|
||||||
|
fd_set errorfds_orig;
|
||||||
|
} cdcacm_select_args_t;
|
||||||
|
|
||||||
|
static cdcacm_select_args_t **s_registered_selects = NULL;
|
||||||
|
static int s_registered_select_num = 0;
|
||||||
|
static portMUX_TYPE s_registered_select_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
|
static esp_err_t cdcacm_end_select(void *end_select_args);
|
||||||
|
|
||||||
|
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||||
|
|
||||||
static ssize_t cdcacm_write(int fd, const void *data, size_t size)
|
static ssize_t cdcacm_write(int fd, const void *data, size_t size)
|
||||||
{
|
{
|
||||||
assert(fd == 0);
|
assert(fd == 0);
|
||||||
@@ -82,7 +112,7 @@ static int cdcacm_fsync(int fd)
|
|||||||
|
|
||||||
static int cdcacm_open(const char *path, int flags, int mode)
|
static int cdcacm_open(const char *path, int flags, int mode)
|
||||||
{
|
{
|
||||||
return 0; // fd 0
|
return USB_CDC_LOCAL_FD; // fd 0
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cdcacm_fstat(int fd, struct stat *st)
|
static int cdcacm_fstat(int fd, struct stat *st)
|
||||||
@@ -290,6 +320,167 @@ static int cdcacm_fcntl(int fd, int cmd, int arg)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||||
|
|
||||||
|
static void select_notif_callback_isr(cdcacm_select_notif_t cdcacm_select_notif, BaseType_t *task_woken)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL_ISR(&s_registered_select_lock);
|
||||||
|
for (int i = 0; i < s_registered_select_num; ++i) {
|
||||||
|
cdcacm_select_args_t *args = s_registered_selects[i];
|
||||||
|
if (args) {
|
||||||
|
switch (cdcacm_select_notif) {
|
||||||
|
case CDCACM_SELECT_READ_NOTIF:
|
||||||
|
if (FD_ISSET(USB_CDC_LOCAL_FD, &args->readfds_orig)) {
|
||||||
|
FD_SET(USB_CDC_LOCAL_FD, args->readfds);
|
||||||
|
esp_vfs_select_triggered_isr(args->select_sem, task_woken);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CDCACM_SELECT_WRITE_NOTIF:
|
||||||
|
if (FD_ISSET(USB_CDC_LOCAL_FD, &args->writefds_orig)) {
|
||||||
|
FD_SET(USB_CDC_LOCAL_FD, args->writefds);
|
||||||
|
esp_vfs_select_triggered_isr(args->select_sem, task_woken);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CDCACM_SELECT_ERROR_NOTIF:
|
||||||
|
if (FD_ISSET(USB_CDC_LOCAL_FD, &args->errorfds_orig)) {
|
||||||
|
FD_SET(USB_CDC_LOCAL_FD, args->errorfds);
|
||||||
|
esp_vfs_select_triggered_isr(args->select_sem, task_woken);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL_ISR(&s_registered_select_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t register_select(cdcacm_select_args_t *args)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_ERR_INVALID_ARG;
|
||||||
|
|
||||||
|
if (args) {
|
||||||
|
portENTER_CRITICAL(&s_registered_select_lock);
|
||||||
|
const int new_size = s_registered_select_num + 1;
|
||||||
|
cdcacm_select_args_t **new_selects;
|
||||||
|
if ((new_selects = heap_caps_realloc(s_registered_selects, new_size * sizeof(cdcacm_select_args_t *), CDCACM_VFS_MALLOC_FLAGS)) == NULL) {
|
||||||
|
ret = ESP_ERR_NO_MEM;
|
||||||
|
} else {
|
||||||
|
/* on first select registration register the callback */
|
||||||
|
if (s_registered_select_num == 0) {
|
||||||
|
cdcacm_set_select_notif_callback(select_notif_callback_isr);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_registered_selects = new_selects;
|
||||||
|
s_registered_selects[s_registered_select_num] = args;
|
||||||
|
s_registered_select_num = new_size;
|
||||||
|
ret = ESP_OK;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_registered_select_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t unregister_select(cdcacm_select_args_t *args)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
if (args) {
|
||||||
|
ret = ESP_ERR_INVALID_STATE;
|
||||||
|
portENTER_CRITICAL(&s_registered_select_lock);
|
||||||
|
for (int i = 0; i < s_registered_select_num; ++i) {
|
||||||
|
if (s_registered_selects[i] == args) {
|
||||||
|
const int new_size = s_registered_select_num - 1;
|
||||||
|
// The item is removed by overwriting it with the last item. The subsequent rellocation will drop the
|
||||||
|
// last item.
|
||||||
|
s_registered_selects[i] = s_registered_selects[new_size];
|
||||||
|
s_registered_selects = heap_caps_realloc(s_registered_selects, new_size * sizeof(cdcacm_select_args_t *), CDCACM_VFS_MALLOC_FLAGS);
|
||||||
|
// Shrinking a buffer with realloc is guaranteed to succeed.
|
||||||
|
s_registered_select_num = new_size;
|
||||||
|
|
||||||
|
/* when the last select is unregistered, also unregister the callback */
|
||||||
|
if (s_registered_select_num == 0) {
|
||||||
|
cdcacm_set_select_notif_callback(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ESP_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_registered_select_lock);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t cdcacm_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
|
esp_vfs_select_sem_t select_sem, void **end_select_args)
|
||||||
|
{
|
||||||
|
(void)nfds; /* Since there is only 1 USB OTG, this parameter is useless */
|
||||||
|
*end_select_args = NULL;
|
||||||
|
|
||||||
|
if (!esp_usb_console_is_installed()) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cdcacm_select_args_t *args = heap_caps_malloc(sizeof(cdcacm_select_args_t), CDCACM_VFS_MALLOC_FLAGS);
|
||||||
|
|
||||||
|
if (args == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
args->select_sem = select_sem;
|
||||||
|
args->readfds = readfds;
|
||||||
|
args->writefds = writefds;
|
||||||
|
args->errorfds = exceptfds;
|
||||||
|
args->readfds_orig = *readfds; // store the original values because they will be set to zero
|
||||||
|
args->writefds_orig = *writefds;
|
||||||
|
args->errorfds_orig = *exceptfds;
|
||||||
|
FD_ZERO(readfds);
|
||||||
|
FD_ZERO(writefds);
|
||||||
|
FD_ZERO(exceptfds);
|
||||||
|
|
||||||
|
esp_err_t ret = register_select(args);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
free(args);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trigger_select = false;
|
||||||
|
if (FD_ISSET(USB_CDC_LOCAL_FD, &args->readfds_orig) &&
|
||||||
|
esp_usb_console_available_for_read() > 0) {
|
||||||
|
|
||||||
|
// signalize immediately when read is ready
|
||||||
|
FD_SET(USB_CDC_LOCAL_FD, readfds);
|
||||||
|
trigger_select = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FD_ISSET(USB_CDC_LOCAL_FD, &args->writefds_orig) &&
|
||||||
|
esp_usb_console_write_available()) {
|
||||||
|
|
||||||
|
// signalize immediately when write is ready
|
||||||
|
FD_SET(USB_CDC_LOCAL_FD, writefds);
|
||||||
|
trigger_select = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trigger_select) {
|
||||||
|
esp_vfs_select_triggered(args->select_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
*end_select_args = args;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t cdcacm_end_select(void *end_select_args)
|
||||||
|
{
|
||||||
|
cdcacm_select_args_t *args = end_select_args;
|
||||||
|
esp_err_t ret = unregister_select(args);
|
||||||
|
if (args) {
|
||||||
|
free(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||||
|
|
||||||
void esp_vfs_dev_cdcacm_set_tx_line_endings(esp_line_endings_t mode)
|
void esp_vfs_dev_cdcacm_set_tx_line_endings(esp_line_endings_t mode)
|
||||||
{
|
{
|
||||||
s_tx_mode = mode;
|
s_tx_mode = mode;
|
||||||
@@ -300,6 +491,13 @@ void esp_vfs_dev_cdcacm_set_rx_line_endings(esp_line_endings_t mode)
|
|||||||
s_rx_mode = mode;
|
s_rx_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||||
|
static const esp_vfs_select_ops_t s_cdcacm_vfs_select = {
|
||||||
|
.start_select = &cdcacm_start_select,
|
||||||
|
.end_select = &cdcacm_end_select,
|
||||||
|
};
|
||||||
|
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||||
|
|
||||||
static const esp_vfs_fs_ops_t s_cdcacm_vfs = {
|
static const esp_vfs_fs_ops_t s_cdcacm_vfs = {
|
||||||
.write = &cdcacm_write,
|
.write = &cdcacm_write,
|
||||||
.open = &cdcacm_open,
|
.open = &cdcacm_open,
|
||||||
@@ -307,7 +505,10 @@ static const esp_vfs_fs_ops_t s_cdcacm_vfs = {
|
|||||||
.close = &cdcacm_close,
|
.close = &cdcacm_close,
|
||||||
.read = &cdcacm_read,
|
.read = &cdcacm_read,
|
||||||
.fcntl = &cdcacm_fcntl,
|
.fcntl = &cdcacm_fcntl,
|
||||||
.fsync = &cdcacm_fsync
|
.fsync = &cdcacm_fsync,
|
||||||
|
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||||
|
.select = &s_cdcacm_vfs_select,
|
||||||
|
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||||
};
|
};
|
||||||
|
|
||||||
const esp_vfs_fs_ops_t *esp_vfs_cdcacm_get_vfs(void)
|
const esp_vfs_fs_ops_t *esp_vfs_cdcacm_get_vfs(void)
|
||||||
|
Reference in New Issue
Block a user