mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-04 09:01:40 +01:00 
			
		
		
		
	
		
			
	
	
		
			325 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			325 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Licensed under the Apache License, Version 2.0 (the "License");
							 | 
						||
| 
								 | 
							
								// you may not use this file except in compliance with the License.
							 | 
						||
| 
								 | 
							
								// You may obtain a copy of the License at
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//     http://www.apache.org/licenses/LICENSE-2.0
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Unless required by applicable law or agreed to in writing, software
							 | 
						||
| 
								 | 
							
								// distributed under the License is distributed on an "AS IS" BASIS,
							 | 
						||
| 
								 | 
							
								// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
							 | 
						||
| 
								 | 
							
								// See the License for the specific language governing permissions and
							 | 
						||
| 
								 | 
							
								// limitations under the License.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <string.h>
							 | 
						||
| 
								 | 
							
								#include <stdbool.h>
							 | 
						||
| 
								 | 
							
								#include <stdarg.h>
							 | 
						||
| 
								 | 
							
								#include <sys/errno.h>
							 | 
						||
| 
								 | 
							
								#include <sys/lock.h>
							 | 
						||
| 
								 | 
							
								#include <sys/fcntl.h>
							 | 
						||
| 
								 | 
							
								#include <sys/param.h>
							 | 
						||
| 
								 | 
							
								#include "esp_vfs.h"
							 | 
						||
| 
								 | 
							
								#include "esp_vfs_cdcacm.h"
							 | 
						||
| 
								 | 
							
								#include "esp_attr.h"
							 | 
						||
| 
								 | 
							
								#include "sdkconfig.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "esp_private/usb_console.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Newline conversion mode when transmitting
							 | 
						||
| 
								 | 
							
								static esp_line_endings_t s_tx_mode =
							 | 
						||
| 
								 | 
							
								#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
							 | 
						||
| 
								 | 
							
								    ESP_LINE_ENDINGS_CRLF;
							 | 
						||
| 
								 | 
							
								#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR
							 | 
						||
| 
								 | 
							
								    ESP_LINE_ENDINGS_CR;
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								    ESP_LINE_ENDINGS_LF;
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Newline conversion mode when receiving
							 | 
						||
| 
								 | 
							
								static esp_line_endings_t s_rx_mode =
							 | 
						||
| 
								 | 
							
								#if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF
							 | 
						||
| 
								 | 
							
								    ESP_LINE_ENDINGS_CRLF;
							 | 
						||
| 
								 | 
							
								#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR
							 | 
						||
| 
								 | 
							
								    ESP_LINE_ENDINGS_CR;
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								    ESP_LINE_ENDINGS_LF;
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define NONE -1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//Read and write lock, lazily initialized
							 | 
						||
| 
								 | 
							
								static _lock_t s_write_lock;
							 | 
						||
| 
								 | 
							
								static _lock_t s_read_lock;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static bool s_blocking;
							 | 
						||
| 
								 | 
							
								static SemaphoreHandle_t s_rx_semaphore;
							 | 
						||
| 
								 | 
							
								static SemaphoreHandle_t s_tx_semaphore;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static ssize_t cdcacm_write(int fd, const void *data, size_t size)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    assert(fd == 0);
							 | 
						||
| 
								 | 
							
								    const char *cdata = (const char *)data;
							 | 
						||
| 
								 | 
							
								    _lock_acquire_recursive(&s_write_lock);
							 | 
						||
| 
								 | 
							
								    for (size_t i = 0; i < size; i++) {
							 | 
						||
| 
								 | 
							
								        if (cdata[i] != '\n') {
							 | 
						||
| 
								 | 
							
								            esp_usb_console_write_buf(&cdata[i], 1);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            if (s_tx_mode == ESP_LINE_ENDINGS_CRLF || s_tx_mode == ESP_LINE_ENDINGS_CR) {
							 | 
						||
| 
								 | 
							
								                char cr = '\r';
							 | 
						||
| 
								 | 
							
								                esp_usb_console_write_buf(&cr, 1);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (s_tx_mode == ESP_LINE_ENDINGS_CRLF || s_tx_mode == ESP_LINE_ENDINGS_LF) {
							 | 
						||
| 
								 | 
							
								                char lf = '\n';
							 | 
						||
| 
								 | 
							
								                esp_usb_console_write_buf(&lf, 1);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _lock_release_recursive(&s_write_lock);
							 | 
						||
| 
								 | 
							
								    return size;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int cdcacm_fsync(int fd)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    assert(fd == 0);
							 | 
						||
| 
								 | 
							
								    _lock_acquire_recursive(&s_write_lock);
							 | 
						||
| 
								 | 
							
								    ssize_t written = esp_usb_console_flush();
							 | 
						||
| 
								 | 
							
								    _lock_release_recursive(&s_write_lock);
							 | 
						||
| 
								 | 
							
								    return (written < 0) ? -1 : 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int cdcacm_open(const char *path, int flags, int mode)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    return 0; // fd 0
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int cdcacm_fstat(int fd, struct stat *st)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    assert(fd == 0);
							 | 
						||
| 
								 | 
							
								    st->st_mode = S_IFCHR;
							 | 
						||
| 
								 | 
							
								    return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int cdcacm_close(int fd)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    assert(fd == 0);
							 | 
						||
| 
								 | 
							
								    return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int s_peek_char = NONE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Helper function which returns a previous character or reads a new one from
							 | 
						||
| 
								 | 
							
								 * CDC-ACM driver. Previous character can be returned ("pushed back") using
							 | 
						||
| 
								 | 
							
								 * cdcacm_return_char function. Returns NONE if no character is available. Note
							 | 
						||
| 
								 | 
							
								 * the cdcacm driver maintains its own RX buffer and a receive call does not
							 | 
						||
| 
								 | 
							
								 * invoke an USB operation, so there's no penalty to reading data char-by-char.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								static int cdcacm_read_char(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    /* return character from peek buffer, if it is there */
							 | 
						||
| 
								 | 
							
								    if (s_peek_char != NONE) {
							 | 
						||
| 
								 | 
							
								        int c = s_peek_char;
							 | 
						||
| 
								 | 
							
								        s_peek_char = NONE;
							 | 
						||
| 
								 | 
							
								        return c;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /* Peek buffer is empty; try to read from cdcacm driver. */
							 | 
						||
| 
								 | 
							
								    uint8_t c;
							 | 
						||
| 
								 | 
							
								    ssize_t read = esp_usb_console_read_buf((char *) &c, 1);
							 | 
						||
| 
								 | 
							
								    if (read <= 0) {
							 | 
						||
| 
								 | 
							
								        return NONE;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        return c;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static bool cdcacm_data_in_buffer(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (s_peek_char != NONE) {
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (esp_usb_console_read_available()) {
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Push back a character; it will be returned by next call to cdcacm_read_char */
							 | 
						||
| 
								 | 
							
								static void cdcacm_return_char(int c)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    assert(s_peek_char == NONE);
							 | 
						||
| 
								 | 
							
								    s_peek_char = c;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static ssize_t cdcacm_read(int fd, void *data, size_t size)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    assert(fd == 0);
							 | 
						||
| 
								 | 
							
								    char *data_c = (char *) data;
							 | 
						||
| 
								 | 
							
								    ssize_t received = 0;
							 | 
						||
| 
								 | 
							
								    _lock_acquire_recursive(&s_read_lock);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while (!cdcacm_data_in_buffer()) {
							 | 
						||
| 
								 | 
							
								        if (!s_blocking) {
							 | 
						||
| 
								 | 
							
								            errno = EWOULDBLOCK;
							 | 
						||
| 
								 | 
							
								            _lock_release_recursive(&s_read_lock);
							 | 
						||
| 
								 | 
							
								            return -1;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        xSemaphoreTake(s_rx_semaphore, portMAX_DELAY);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (s_rx_mode == ESP_LINE_ENDINGS_CR || s_rx_mode == ESP_LINE_ENDINGS_LF) {
							 | 
						||
| 
								 | 
							
								        /* This is easy. Just receive, and if needed replace \r by \n. */
							 | 
						||
| 
								 | 
							
								        received = esp_usb_console_read_buf(data_c, size);
							 | 
						||
| 
								 | 
							
								        if (s_rx_mode == ESP_LINE_ENDINGS_CR) {
							 | 
						||
| 
								 | 
							
								            /* Change CRs to newlines */
							 | 
						||
| 
								 | 
							
								            for (ssize_t i = 0; i < received; i++) {
							 | 
						||
| 
								 | 
							
								                if (data_c[i] == '\r') {
							 | 
						||
| 
								 | 
							
								                    data_c[i] = '\n';
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        while (received < size) {
							 | 
						||
| 
								 | 
							
								            int c = cdcacm_read_char();
							 | 
						||
| 
								 | 
							
								            if (c == '\r') {
							 | 
						||
| 
								 | 
							
								                /* look ahead */
							 | 
						||
| 
								 | 
							
								                int c2 = cdcacm_read_char();
							 | 
						||
| 
								 | 
							
								                if (c2 == NONE) {
							 | 
						||
| 
								 | 
							
								                    /* could not look ahead, put the current character back */
							 | 
						||
| 
								 | 
							
								                    cdcacm_return_char(c);
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                if (c2 == '\n') {
							 | 
						||
| 
								 | 
							
								                    /* this was \r\n sequence. discard \r, return \n */
							 | 
						||
| 
								 | 
							
								                    c = '\n';
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    /* \r followed by something else. put the second char back,
							 | 
						||
| 
								 | 
							
								                     * it will be processed on next iteration. return \r now.
							 | 
						||
| 
								 | 
							
								                     */
							 | 
						||
| 
								 | 
							
								                    cdcacm_return_char(c2);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            } else if (c == NONE) {
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            data_c[received++] = (char) c;
							 | 
						||
| 
								 | 
							
								            if (c == '\n') {
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _lock_release_recursive(&s_read_lock);
							 | 
						||
| 
								 | 
							
								    if (received > 0) {
							 | 
						||
| 
								 | 
							
								        return received;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    errno = EWOULDBLOCK;
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Non-static, to be able to place into IRAM by ldgen */
							 | 
						||
| 
								 | 
							
								void cdcacm_rx_cb(void* arg)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    assert(s_blocking);
							 | 
						||
| 
								 | 
							
								    xSemaphoreGive(s_rx_semaphore);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Non-static, to be able to place into IRAM by ldgen */
							 | 
						||
| 
								 | 
							
								void cdcacm_tx_cb(void* arg)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    assert(s_blocking);
							 | 
						||
| 
								 | 
							
								    xSemaphoreGive(s_tx_semaphore);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int cdcacm_enable_blocking(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    s_rx_semaphore = xSemaphoreCreateBinary();
							 | 
						||
| 
								 | 
							
								    if (!s_rx_semaphore) {
							 | 
						||
| 
								 | 
							
								        errno = ENOMEM;
							 | 
						||
| 
								 | 
							
								        goto fail;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    s_tx_semaphore = xSemaphoreCreateBinary();
							 | 
						||
| 
								 | 
							
								    if (!s_tx_semaphore) {
							 | 
						||
| 
								 | 
							
								        errno = ENOMEM;
							 | 
						||
| 
								 | 
							
								        goto fail;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    esp_err_t err = esp_usb_console_set_cb(&cdcacm_rx_cb, &cdcacm_tx_cb, NULL);
							 | 
						||
| 
								 | 
							
								    if (err != ESP_OK) {
							 | 
						||
| 
								 | 
							
								        errno = ENODEV;
							 | 
						||
| 
								 | 
							
								        goto fail;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    s_blocking = true;
							 | 
						||
| 
								 | 
							
								    return 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								fail:
							 | 
						||
| 
								 | 
							
								    if (s_rx_semaphore) {
							 | 
						||
| 
								 | 
							
								        vSemaphoreDelete(s_rx_semaphore);
							 | 
						||
| 
								 | 
							
								        s_rx_semaphore = NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (s_tx_semaphore) {
							 | 
						||
| 
								 | 
							
								        vSemaphoreDelete(s_tx_semaphore);
							 | 
						||
| 
								 | 
							
								        s_tx_semaphore = NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int cdcacm_disable_blocking(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    esp_usb_console_set_cb(NULL, NULL, NULL); /* ignore any errors */
							 | 
						||
| 
								 | 
							
								    vSemaphoreDelete(s_rx_semaphore);
							 | 
						||
| 
								 | 
							
								    s_rx_semaphore = NULL;
							 | 
						||
| 
								 | 
							
								    vSemaphoreDelete(s_tx_semaphore);
							 | 
						||
| 
								 | 
							
								    s_tx_semaphore = NULL;
							 | 
						||
| 
								 | 
							
								    s_blocking = false;
							 | 
						||
| 
								 | 
							
								    return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int cdcacm_fcntl(int fd, int cmd, int arg)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    assert(fd == 0);
							 | 
						||
| 
								 | 
							
								    int result;
							 | 
						||
| 
								 | 
							
								    if (cmd == F_GETFL) {
							 | 
						||
| 
								 | 
							
								        result = 0;
							 | 
						||
| 
								 | 
							
								        if (!s_blocking) {
							 | 
						||
| 
								 | 
							
								            result |= O_NONBLOCK;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    } else if (cmd == F_SETFL) {
							 | 
						||
| 
								 | 
							
								        bool blocking = (arg & O_NONBLOCK) == 0;
							 | 
						||
| 
								 | 
							
								        result = 0;
							 | 
						||
| 
								 | 
							
								        if (blocking && !s_blocking) {
							 | 
						||
| 
								 | 
							
								            result = cdcacm_enable_blocking();
							 | 
						||
| 
								 | 
							
								        } else if (!blocking && s_blocking) {
							 | 
						||
| 
								 | 
							
								            result = cdcacm_disable_blocking();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        /* unsupported operation */
							 | 
						||
| 
								 | 
							
								        result = -1;
							 | 
						||
| 
								 | 
							
								        errno = ENOSYS;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return result;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void esp_vfs_dev_cdcacm_set_tx_line_endings(esp_line_endings_t mode)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    s_tx_mode = mode;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void esp_vfs_dev_cdcacm_set_rx_line_endings(esp_line_endings_t mode)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    s_rx_mode = mode;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								esp_err_t esp_vfs_dev_cdcacm_register(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    const esp_vfs_t vfs = {
							 | 
						||
| 
								 | 
							
								        .flags = ESP_VFS_FLAG_DEFAULT,
							 | 
						||
| 
								 | 
							
								        .write = &cdcacm_write,
							 | 
						||
| 
								 | 
							
								        .open = &cdcacm_open,
							 | 
						||
| 
								 | 
							
								        .fstat = &cdcacm_fstat,
							 | 
						||
| 
								 | 
							
								        .close = &cdcacm_close,
							 | 
						||
| 
								 | 
							
								        .read = &cdcacm_read,
							 | 
						||
| 
								 | 
							
								        .fcntl = &cdcacm_fcntl,
							 | 
						||
| 
								 | 
							
								        .fsync = &cdcacm_fsync
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    return esp_vfs_register("/dev/cdcacm", &vfs, NULL);
							 | 
						||
| 
								 | 
							
								}
							 |