/* * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "esp_err.h" #include "esp_rom_sys.h" #include "esp_vfs_cdcacm.h" #include "esp_vfs_private.h" #include "esp_private/usb_console.h" #include "esp_vfs_console.h" #include "esp_private/esp_vfs_console.h" #include "sdkconfig.h" #include "esp_private/startup_internal.h" #define STRINGIFY(s) STRINGIFY2(s) #define STRINGIFY2(s) #s /** * This file is to concentrate all the vfs(UART, USB_SERIAL_JTAG, CDCACM) console into one single file. * Get the vfs information from their component (i.e. uart_vfs.c), * which can help us to output some string to two different ports(i.e both through uart and usb_serial_jtag). * Usually, we set a port as primary and another as secondary. For primary, it is used for all the features supported by each vfs implementation, * while the secondary is only used for output. */ typedef struct { int fd_primary; int fd_secondary; } vfs_console_context_t; #if CONFIG_VFS_SUPPORT_IO // Primary register part. #ifdef CONFIG_ESP_CONSOLE_UART const static char *primary_path = "/dev/uart"; #elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG const static char *primary_path = "/dev/usbserjtag"; #elif CONFIG_ESP_CONSOLE_USB_CDC const static char *primary_path = "/dev/cdcacm"; #endif // Secondary register part. #if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG const static char *secondary_path = "/dev/secondary"; static int secondary_vfs_index; const static esp_vfs_t *secondary_vfs = NULL; #endif // Secondary part static int primary_vfs_index; const static esp_vfs_t *primary_vfs = NULL; static vfs_console_context_t vfs_console= {0}; int console_open(const char * path, int flags, int mode) { // Primary port open #if CONFIG_ESP_CONSOLE_UART vfs_console.fd_primary = get_vfs_for_path(primary_path)->vfs.open("/"STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM), flags, mode); #elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG vfs_console.fd_primary = get_vfs_for_path(primary_path)->vfs.open("/", flags, mode); #elif CONFIG_ESP_CONSOLE_USB_CDC vfs_console.fd_primary = esp_vfs_cdcacm_get_vfs()->open("/", flags, mode); #endif // Secondary port open #if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG vfs_console.fd_secondary = get_vfs_for_path(secondary_path)->vfs.open("/", flags, mode); #endif return 0; } ssize_t console_write(int fd, const void *data, size_t size) { // All function calls are to primary, except from write and close, which will be forwarded to both primary and secondary. get_vfs_for_index(primary_vfs_index)->vfs.write(vfs_console.fd_primary, data, size); #if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG get_vfs_for_index(secondary_vfs_index)->vfs.write(vfs_console.fd_secondary, data, size); #endif return size; } int console_fstat(int fd, struct stat * st) { return get_vfs_for_index(primary_vfs_index)->vfs.fstat(fd, st); } int console_close(int fd) { // All function calls are to primary, except from write and close, which will be forwarded to both primary and secondary. get_vfs_for_index(primary_vfs_index)->vfs.close(vfs_console.fd_primary); #if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG get_vfs_for_index(secondary_vfs_index)->vfs.close(vfs_console.fd_secondary); #endif return 0; } ssize_t console_read(int fd, void * dst, size_t size) { return get_vfs_for_index(primary_vfs_index)->vfs.read(vfs_console.fd_primary, dst, size); } int console_fcntl(int fd, int cmd, int arg) { return get_vfs_for_index(primary_vfs_index)->vfs.fcntl(vfs_console.fd_primary, cmd, arg); } int console_fsync(int fd) { return get_vfs_for_index(primary_vfs_index)->vfs.fsync(vfs_console.fd_primary); } #ifdef CONFIG_VFS_SUPPORT_DIR int console_access(const char *path, int amode) { // currently only UART support DIR. return get_vfs_for_index(primary_vfs_index)->vfs.access("/"STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM), amode); } #endif // CONFIG_VFS_SUPPORT_DIR #ifdef CONFIG_VFS_SUPPORT_SELECT static esp_err_t console_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, esp_vfs_select_sem_t select_sem, void **end_select_args) { const vfs_entry_t* vfs_entry = get_vfs_for_index(primary_vfs_index); // start_select is not guaranteed be implemented even though CONFIG_VFS_SUPPORT_SELECT is enabled in sdkconfig if (vfs_entry && vfs_entry->vfs.start_select) { return vfs_entry->vfs.start_select(nfds, readfds, writefds, exceptfds, select_sem, end_select_args); } return ESP_ERR_NOT_SUPPORTED; } esp_err_t console_end_select(void *end_select_args) { const vfs_entry_t* vfs_entry = get_vfs_for_index(primary_vfs_index); // end_select is not guaranteed be implemented even though CONFIG_VFS_SUPPORT_SELECT is enabled in sdkconfig if (vfs_entry && vfs_entry->vfs.end_select) { return vfs_entry->vfs.end_select(end_select_args); } return ESP_ERR_NOT_SUPPORTED; } #endif // CONFIG_VFS_SUPPORT_SELECT #ifdef CONFIG_VFS_SUPPORT_TERMIOS int console_tcsetattr(int fd, int optional_actions, const struct termios *p) { return get_vfs_for_index(primary_vfs_index)->vfs.tcsetattr(vfs_console.fd_primary, optional_actions, p); } int console_tcgetattr(int fd, struct termios *p) { return get_vfs_for_index(primary_vfs_index)->vfs.tcgetattr(vfs_console.fd_primary, p); } int console_tcdrain(int fd) { return get_vfs_for_index(primary_vfs_index)->vfs.tcdrain(vfs_console.fd_primary); } int console_tcflush(int fd, int select) { return get_vfs_for_index(primary_vfs_index)->vfs.tcflush(vfs_console.fd_primary, select); } #endif // CONFIG_VFS_SUPPORT_TERMIOS static const esp_vfs_t vfs = { .flags = ESP_VFS_FLAG_DEFAULT, .write = &console_write, .open = &console_open, .fstat = &console_fstat, .close = &console_close, .read = &console_read, .fcntl = &console_fcntl, .fsync = &console_fsync, #ifdef CONFIG_VFS_SUPPORT_DIR .access = &console_access, #endif // CONFIG_VFS_SUPPORT_DIR #ifdef CONFIG_VFS_SUPPORT_SELECT .start_select = &console_start_select, .end_select = &console_end_select, #endif // CONFIG_VFS_SUPPORT_SELECT #ifdef CONFIG_VFS_SUPPORT_TERMIOS .tcsetattr = &console_tcsetattr, .tcgetattr = &console_tcgetattr, .tcdrain = &console_tcdrain, .tcflush = &console_tcflush, #endif // CONFIG_VFS_SUPPORT_TERMIOS }; static esp_err_t esp_vfs_dev_console_register(void) { return esp_vfs_register(ESP_VFS_DEV_CONSOLE, &vfs, NULL); } esp_err_t esp_vfs_console_register(void) { esp_err_t err = ESP_OK; // Primary register part. #if CONFIG_ESP_CONSOLE_UART || CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG assert(primary_vfs); #elif CONFIG_ESP_CONSOLE_USB_CDC primary_vfs = esp_vfs_cdcacm_get_vfs(); err = esp_usb_console_init(); if (err != ESP_OK) { return err; } #endif #if !CONFIG_ESP_CONSOLE_NONE err = esp_vfs_register_common(primary_path, strlen(primary_path), primary_vfs, NULL, &primary_vfs_index); if (err != ESP_OK) { return err; } #endif // !CONFIG_ESP_CONSOLE_NONE // Secondary register part. #if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG assert(secondary_vfs); err = esp_vfs_register_common(secondary_path, strlen(secondary_path), secondary_vfs, NULL, &secondary_vfs_index); if(err != ESP_OK) { return err; } #endif err = esp_vfs_dev_console_register(); return err; } void esp_vfs_set_primary_dev_vfs_def_struct(const esp_vfs_t *vfs) { primary_vfs = vfs; } #if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG void esp_vfs_set_secondary_dev_vfs_def_struct(const esp_vfs_t *vfs) { secondary_vfs = vfs; } #endif ESP_SYSTEM_INIT_FN(init_vfs_console, CORE, BIT(0), 114) { return esp_vfs_console_register(); } #endif // CONFIG_VFS_SUPPORT_IO void esp_vfs_include_console_register(void) { // Linker hook function, exists to make the linker examine this file }