ESP-IDF Firmware
Firmware architecture and call graph
Loading...
Searching...
No Matches
vfs_tinyusb.c
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#include <stdarg.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdio_ext.h>
11#include <string.h>
12#include <sys/errno.h>
13#include <sys/fcntl.h>
14#include <sys/lock.h>
15#include <sys/param.h>
16#include "esp_attr.h"
17#include "esp_log.h"
18#include "esp_vfs.h"
19#include "esp_vfs_dev.h"
20#include "tinyusb.h"
21#include "tusb_cdc_acm.h"
22#include "vfs_tinyusb.h"
23#include "sdkconfig.h"
24
25const static char *TAG = "tusb_vfs";
26
27// Token signifying that no character is available
28#define NONE -1
29
30#define FD_CHECK(fd, ret_val) do { \
31 if ((fd) != 0) { \
32 errno = EBADF; \
33 return (ret_val); \
34 } \
35 } while (0)
36
37
38
39#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
40# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CRLF
41#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR
42# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CR
43#else
44# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_LF
45#endif
46
47#if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF
48# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CRLF
49#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR
50# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CR
51#else
52# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_LF
53#endif
54
55typedef struct {
56 _lock_t write_lock;
57 _lock_t read_lock;
58 esp_line_endings_t tx_mode; // Newline conversion mode when transmitting
59 esp_line_endings_t rx_mode; // Newline conversion mode when receiving
60 uint32_t flags;
64
66
67
68static esp_err_t apply_path(char const *path)
69{
70 if (path != NULL) {
71 size_t path_len = strlen(path) + 1;
72 if (path_len > VFS_TUSB_MAX_PATH) {
73 ESP_LOGE(TAG, "The path is too long; maximum is %d characters", VFS_TUSB_MAX_PATH);
74 return ESP_ERR_INVALID_ARG;
75 }
76 strncpy(s_vfstusb.vfs_path, path, (VFS_TUSB_MAX_PATH - 1));
77 } else {
78 strncpy(s_vfstusb.vfs_path,
80 (VFS_TUSB_MAX_PATH - 1));
81 }
82 ESP_LOGV(TAG, "Path is set to `%s`", s_vfstusb.vfs_path);
83 return ESP_OK;
84}
85
93static esp_err_t vfstusb_init(int cdc_intf, char const *path)
94{
95 s_vfstusb.cdc_intf = cdc_intf;
96 s_vfstusb.tx_mode = DEFAULT_TX_MODE;
97 s_vfstusb.rx_mode = DEFAULT_RX_MODE;
98
99 return apply_path(path);
100}
101
105static void vfstusb_deinit(void)
106{
107 memset(&s_vfstusb, 0, sizeof(s_vfstusb));
108}
109
110
111static int tusb_open(const char *path, int flags, int mode)
112{
113 (void) mode;
114 (void) path;
115 s_vfstusb.flags = flags | O_NONBLOCK; // for now only non-blocking mode is implemented
116 return 0;
117}
118
119static ssize_t tusb_write(int fd, const void *data, size_t size)
120{
121 FD_CHECK(fd, -1);
122 size_t written_sz = 0;
123 const char *data_c = (const char *)data;
124 _lock_acquire(&(s_vfstusb.write_lock));
125 for (size_t i = 0; i < size; i++) {
126 int c = data_c[i];
127 /* handling the EOL */
128 if (c == '\n' && s_vfstusb.tx_mode != ESP_LINE_ENDINGS_LF) {
129 if (tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, '\r')) {
130 written_sz++;
131 } else {
132 break; // can't write anymore
133 }
134 if (s_vfstusb.tx_mode == ESP_LINE_ENDINGS_CR) {
135 continue;
136 }
137 }
138 /* write a char */
140 written_sz++;
141 } else {
142 break; // can't write anymore
143 }
144
145 }
146 tud_cdc_n_write_flush(s_vfstusb.cdc_intf);
147 _lock_release(&(s_vfstusb.write_lock));
148 return written_sz;
149}
150
151static int tusb_close(int fd)
152{
153 FD_CHECK(fd, -1);
154 return 0;
155}
156
157static ssize_t tusb_read(int fd, void *data, size_t size)
158{
159 FD_CHECK(fd, -1);
160 char *data_c = (char *) data;
161 size_t received = 0;
162 _lock_acquire(&(s_vfstusb.read_lock));
163 int cm1 = NONE;
164 int c = NONE;
165
166 while (received < size) {
167 cm1 = c; // store the old char
168 int c = tud_cdc_n_read_char(0); // get a new one
169 if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) {
170 if (c == '\r') {
171 c = '\n';
172 }
173 } else if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) {
174 if ((c == '\n') & (cm1 == '\r')) {
175 --received; // step back
176 c = '\n';
177 }
178 }
179 if ( c == NONE) { // if data ends
180 break;
181 }
182 data_c[received] = (char) c;
183 ++received;
184 if (c == '\n') {
185 break;
186 }
187 }
188 _lock_release(&(s_vfstusb.read_lock));
189 if (received > 0) {
190 return received;
191 }
192 errno = EWOULDBLOCK;
193 return -1;
194}
195
196
197static int tusb_fstat(int fd, struct stat *st)
198{
199 FD_CHECK(fd, -1);
200 memset(st, 0, sizeof(*st));
201 st->st_mode = S_IFCHR;
202 return 0;
203}
204
205static int tusb_fcntl(int fd, int cmd, int arg)
206{
207 FD_CHECK(fd, -1);
208 int result = 0;
209 switch (cmd) {
210 case F_GETFL:
211 result = s_vfstusb.flags;
212 break;
213 case F_SETFL:
214 s_vfstusb.flags = arg;
215 break;
216 default:
217 result = -1;
218 errno = ENOSYS;
219 break;
220 }
221 return result;
222}
223
225{
226 ESP_LOGD(TAG, "Unregistering TinyUSB driver");
227 int res;
228
229 if (path == NULL) { // NULL means using the default path for unregistering: VFS_TUSB_PATH_DEFAULT
230 res = strcmp(s_vfstusb.vfs_path, VFS_TUSB_PATH_DEFAULT);
231 } else {
232 res = strcmp(s_vfstusb.vfs_path, path);
233 }
234
235 if (res) {
236 res = ESP_ERR_INVALID_ARG;
237 ESP_LOGE(TAG, "There is no TinyUSB driver registerred to the path '%s' (err: 0x%x)", s_vfstusb.vfs_path, res);
238 return res;
239 }
240
241
242
243 res = esp_vfs_unregister(s_vfstusb.vfs_path);
244 if (res != ESP_OK) {
245 ESP_LOGE(TAG, "Can't unregister TinyUSB driver from '%s' (err: 0x%x)", s_vfstusb.vfs_path, res);
246 } else {
247 ESP_LOGD(TAG, "Unregistered TinyUSB driver");
249 }
250 return res;
251}
252
253
254
255
256esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path)
257{
258 ESP_LOGD(TAG, "Registering TinyUSB CDC driver");
259 int res;
260 if (!tusb_cdc_acm_initialized(cdc_intf)) {
261 ESP_LOGE(TAG, "TinyUSB CDC#%d is not initialized", cdc_intf);
262 return ESP_ERR_INVALID_STATE;
263 }
264
265 res = vfstusb_init(cdc_intf, path);
266 if (res != ESP_OK) {
267 return res;
268 }
269
270 esp_vfs_t vfs = {
271 .flags = ESP_VFS_FLAG_DEFAULT,
272 .close = &tusb_close,
273 .fcntl = &tusb_fcntl,
274 .fstat = &tusb_fstat,
275 .open = &tusb_open,
276 .read = &tusb_read,
277 .write = &tusb_write,
278 };
279
280 res = esp_vfs_register(s_vfstusb.vfs_path, &vfs, NULL);
281 if (res != ESP_OK) {
282 ESP_LOGE(TAG, "Can't register TinyUSB driver (err: %x)", res);
283 } else {
284 ESP_LOGD(TAG, "TinyUSB CDC registered (%s)", s_vfstusb.vfs_path);
285 }
286 return res;
287}
int esp_err_t
Definition esp_err.h:21
#define ESP_OK
Definition esp_err.h:23
#define ESP_LOGD
Definition esp_log.h:22
static const char * TAG
Definition main/main.c:31
esp_line_endings_t rx_mode
Definition vfs_tinyusb.c:59
_lock_t read_lock
Definition vfs_tinyusb.c:57
_lock_t write_lock
Definition vfs_tinyusb.c:56
esp_line_endings_t tx_mode
Definition vfs_tinyusb.c:58
uint32_t flags
Definition vfs_tinyusb.c:60
char vfs_path[VFS_TUSB_MAX_PATH]
Definition vfs_tinyusb.c:61
static float data[128 *2]
Definition test_fft2r.c:34
bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf)
Check if the ACM initialized.
size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch)
Sent one character to a write buffer.
static int tusb_close(int fd)
esp_err_t esp_vfs_tusb_cdc_unregister(char const *path)
Unregister TinyUSB CDC from VFS.
static esp_err_t apply_path(char const *path)
Definition vfs_tinyusb.c:68
#define NONE
Definition vfs_tinyusb.c:28
static vfs_tinyusb_t s_vfstusb
Definition vfs_tinyusb.c:65
static int tusb_open(const char *path, int flags, int mode)
static esp_err_t vfstusb_init(int cdc_intf, char const *path)
Fill s_vfstusb.
Definition vfs_tinyusb.c:93
static void vfstusb_deinit(void)
Clear s_vfstusb to default values.
static int tusb_fcntl(int fd, int cmd, int arg)
static int tusb_fstat(int fd, struct stat *st)
#define DEFAULT_TX_MODE
Definition vfs_tinyusb.c:44
static ssize_t tusb_read(int fd, void *data, size_t size)
static ssize_t tusb_write(int fd, const void *data, size_t size)
#define DEFAULT_RX_MODE
Definition vfs_tinyusb.c:52
esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path)
Register TinyUSB CDC at VFS with path.
#define FD_CHECK(fd, ret_val)
Definition vfs_tinyusb.c:30
#define VFS_TUSB_PATH_DEFAULT
Definition vfs_tinyusb.h:16
#define VFS_TUSB_MAX_PATH
Definition vfs_tinyusb.h:15