vfs_tinyusb.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. // Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <stdarg.h>
  15. #include <stdbool.h>
  16. #include <stdio.h>
  17. #include <stdio_ext.h>
  18. #include <string.h>
  19. #include <sys/errno.h>
  20. #include <sys/fcntl.h>
  21. #include <sys/lock.h>
  22. #include <sys/param.h>
  23. #include "esp_attr.h"
  24. #include "esp_log.h"
  25. #include "esp_vfs.h"
  26. #include "esp_vfs_dev.h"
  27. #include "tinyusb.h"
  28. #include "tusb_cdc_acm.h"
  29. #include "vfs_tinyusb.h"
  30. #include "sdkconfig.h"
  31. const static char *TAG = "tusb_vfs";
  32. #define VFS_TUSB_MAX_PATH 16
  33. #define VFS_TUSB_PATH_DEFAULT "/dev/tusb_cdc"
  34. // Token signifying that no character is available
  35. #define NONE -1
  36. #define FD_CHECK(fd, ret_val) do { \
  37. if ((fd) != 0) { \
  38. errno = EBADF; \
  39. return (ret_val); \
  40. } \
  41. } while (0)
  42. #if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
  43. # define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CRLF
  44. #elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR
  45. # define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CR
  46. #else
  47. # define DEFAULT_TX_MODE ESP_LINE_ENDINGS_LF
  48. #endif
  49. #if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF
  50. # define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CRLF
  51. #elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR
  52. # define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CR
  53. #else
  54. # define DEFAULT_RX_MODE ESP_LINE_ENDINGS_LF
  55. #endif
  56. typedef struct {
  57. _lock_t write_lock;
  58. _lock_t read_lock;
  59. esp_line_endings_t tx_mode; // Newline conversion mode when transmitting
  60. esp_line_endings_t rx_mode; // Newline conversion mode when receiving
  61. uint32_t flags;
  62. char vfs_path[VFS_TUSB_MAX_PATH];
  63. int cdc_intf;
  64. } vfs_tinyusb_t;
  65. static vfs_tinyusb_t s_vfstusb;
  66. static esp_err_t apply_path(char const *path)
  67. {
  68. if (path != NULL) {
  69. size_t path_len = strlen(path) + 1;
  70. if (path_len > VFS_TUSB_MAX_PATH) {
  71. ESP_LOGE(TAG, "The path is too long; maximum is %d characters", VFS_TUSB_MAX_PATH);
  72. return ESP_ERR_INVALID_ARG;
  73. }
  74. strncpy(s_vfstusb.vfs_path, path, (VFS_TUSB_MAX_PATH - 1));
  75. } else {
  76. strncpy(s_vfstusb.vfs_path,
  77. VFS_TUSB_PATH_DEFAULT,
  78. (VFS_TUSB_MAX_PATH - 1));
  79. }
  80. ESP_LOGV(TAG, "Path is set to `%s`", s_vfstusb.vfs_path);
  81. return ESP_OK;
  82. }
  83. /**
  84. * @brief Fill s_vfstusb
  85. *
  86. * @param cdc_intf - interface of tusb for registration
  87. * @param path - a path where the CDC will be registered
  88. * @return esp_err_t ESP_OK or ESP_ERR_INVALID_ARG
  89. */
  90. static esp_err_t vfstusb_init(int cdc_intf, char const *path)
  91. {
  92. s_vfstusb.cdc_intf = cdc_intf;
  93. s_vfstusb.tx_mode = DEFAULT_TX_MODE;
  94. s_vfstusb.rx_mode = DEFAULT_RX_MODE;
  95. return apply_path(path);
  96. }
  97. /**
  98. * @brief Clear s_vfstusb to default values
  99. */
  100. static void vfstusb_deinit(void)
  101. {
  102. memset(&s_vfstusb, 0, sizeof(s_vfstusb));
  103. }
  104. static int tusb_open(const char *path, int flags, int mode)
  105. {
  106. (void) mode;
  107. (void) path;
  108. s_vfstusb.flags = flags | O_NONBLOCK; // for now only non-blocking mode is implemented
  109. return 0;
  110. }
  111. static ssize_t tusb_write(int fd, const void *data, size_t size)
  112. {
  113. FD_CHECK(fd, -1);
  114. size_t written_sz = 0;
  115. const char *data_c = (const char *)data;
  116. _lock_acquire(&(s_vfstusb.write_lock));
  117. for (size_t i = 0; i < size; i++) {
  118. int c = data_c[i];
  119. /* handling the EOL */
  120. if (c == '\n' && s_vfstusb.tx_mode != ESP_LINE_ENDINGS_LF) {
  121. if (tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, '\r')) {
  122. written_sz++;
  123. } else {
  124. break; // can't write anymore
  125. }
  126. if (s_vfstusb.tx_mode == ESP_LINE_ENDINGS_CR) {
  127. continue;
  128. }
  129. }
  130. /* write a char */
  131. if (tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, c)) {
  132. written_sz++;
  133. } else {
  134. break; // can't write anymore
  135. }
  136. }
  137. tud_cdc_n_write_flush(s_vfstusb.cdc_intf);
  138. _lock_release(&(s_vfstusb.write_lock));
  139. return written_sz;
  140. }
  141. static int tusb_close(int fd)
  142. {
  143. FD_CHECK(fd, -1);
  144. return 0;
  145. }
  146. static ssize_t tusb_read(int fd, void *data, size_t size)
  147. {
  148. FD_CHECK(fd, -1);
  149. char *data_c = (char *) data;
  150. size_t received = 0;
  151. _lock_acquire(&(s_vfstusb.read_lock));
  152. int cm1 = NONE;
  153. int c = NONE;
  154. while (received < size) {
  155. cm1 = c; // store the old char
  156. int c = tud_cdc_n_read_char(0); // get a new one
  157. if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) {
  158. if (c == '\r') {
  159. c = '\n';
  160. }
  161. } else if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) {
  162. if ((c == '\n') & (cm1 == '\r')) {
  163. --received; // step back
  164. c = '\n';
  165. }
  166. }
  167. if ( c == NONE) { // if data ends
  168. break;
  169. }
  170. data_c[received] = (char) c;
  171. ++received;
  172. if (c == '\n') {
  173. break;
  174. }
  175. }
  176. _lock_release(&(s_vfstusb.read_lock));
  177. if (received > 0) {
  178. return received;
  179. }
  180. errno = EWOULDBLOCK;
  181. return -1;
  182. }
  183. static int tusb_fstat(int fd, struct stat *st)
  184. {
  185. FD_CHECK(fd, -1);
  186. memset(st, 0, sizeof(*st));
  187. st->st_mode = S_IFCHR;
  188. return 0;
  189. }
  190. static int tusb_fcntl(int fd, int cmd, int arg)
  191. {
  192. FD_CHECK(fd, -1);
  193. int result = 0;
  194. switch (cmd) {
  195. case F_GETFL:
  196. result = s_vfstusb.flags;
  197. break;
  198. case F_SETFL:
  199. s_vfstusb.flags = arg;
  200. break;
  201. default:
  202. result = -1;
  203. errno = ENOSYS;
  204. break;
  205. }
  206. return result;
  207. }
  208. esp_err_t esp_vfs_tusb_cdc_unregister(char const *path)
  209. {
  210. ESP_LOGD(TAG, "Unregistering TinyUSB driver");
  211. int res;
  212. if (path == NULL) { // NULL means using the default path for unregistering: VFS_TUSB_PATH_DEFAULT
  213. res = strcmp(s_vfstusb.vfs_path, VFS_TUSB_PATH_DEFAULT);
  214. } else {
  215. res = strcmp(s_vfstusb.vfs_path, path);
  216. }
  217. if (res) {
  218. res = ESP_ERR_INVALID_ARG;
  219. ESP_LOGE(TAG, "There is no TinyUSB driver registerred to the path '%s' (err: 0x%x)", s_vfstusb.vfs_path, res);
  220. return res;
  221. }
  222. res = esp_vfs_unregister(s_vfstusb.vfs_path);
  223. if (res != ESP_OK) {
  224. ESP_LOGE(TAG, "Can't unregister TinyUSB driver from '%s' (err: 0x%x)", s_vfstusb.vfs_path, res);
  225. } else {
  226. ESP_LOGD(TAG, "Unregistered TinyUSB driver");
  227. vfstusb_deinit();
  228. }
  229. return res;
  230. }
  231. esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path)
  232. {
  233. ESP_LOGD(TAG, "Registering TinyUSB CDC driver");
  234. int res;
  235. if (!tusb_cdc_acm_initialized(cdc_intf)) {
  236. ESP_LOGE(TAG, "TinyUSB CDC#%d is not initialized", cdc_intf);
  237. return ESP_ERR_INVALID_STATE;
  238. }
  239. res = vfstusb_init(cdc_intf, path);
  240. if (res != ESP_OK) {
  241. return res;
  242. }
  243. esp_vfs_t vfs = {
  244. .flags = ESP_VFS_FLAG_DEFAULT,
  245. .close = &tusb_close,
  246. .fcntl = &tusb_fcntl,
  247. .fstat = &tusb_fstat,
  248. .open = &tusb_open,
  249. .read = &tusb_read,
  250. .write = &tusb_write,
  251. };
  252. res = esp_vfs_register(s_vfstusb.vfs_path, &vfs, NULL);
  253. if (res != ESP_OK) {
  254. ESP_LOGE(TAG, "Can't register TinyUSB driver (err: %x)", res);
  255. } else {
  256. ESP_LOGD(TAG, "TinyUSB CDC registered (%s)", s_vfstusb.vfs_path);
  257. }
  258. return res;
  259. }