vfs_cdcacm.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. // Copyright 2015-2020 Espressif Systems (Shanghai) PTE 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 <string.h>
  15. #include <stdbool.h>
  16. #include <stdarg.h>
  17. #include <sys/errno.h>
  18. #include <sys/lock.h>
  19. #include <sys/fcntl.h>
  20. #include <sys/param.h>
  21. #include "esp_vfs.h"
  22. #include "esp_vfs_cdcacm.h"
  23. #include "esp_attr.h"
  24. #include "sdkconfig.h"
  25. #include "esp_private/usb_console.h"
  26. // Newline conversion mode when transmitting
  27. static esp_line_endings_t s_tx_mode =
  28. #if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
  29. ESP_LINE_ENDINGS_CRLF;
  30. #elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR
  31. ESP_LINE_ENDINGS_CR;
  32. #else
  33. ESP_LINE_ENDINGS_LF;
  34. #endif
  35. // Newline conversion mode when receiving
  36. static esp_line_endings_t s_rx_mode =
  37. #if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF
  38. ESP_LINE_ENDINGS_CRLF;
  39. #elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR
  40. ESP_LINE_ENDINGS_CR;
  41. #else
  42. ESP_LINE_ENDINGS_LF;
  43. #endif
  44. #define NONE -1
  45. //Read and write lock, lazily initialized
  46. static _lock_t s_write_lock;
  47. static _lock_t s_read_lock;
  48. static bool s_blocking;
  49. static SemaphoreHandle_t s_rx_semaphore;
  50. static SemaphoreHandle_t s_tx_semaphore;
  51. static ssize_t cdcacm_write(int fd, const void *data, size_t size)
  52. {
  53. assert(fd == 0);
  54. const char *cdata = (const char *)data;
  55. _lock_acquire_recursive(&s_write_lock);
  56. for (size_t i = 0; i < size; i++) {
  57. if (cdata[i] != '\n') {
  58. esp_usb_console_write_buf(&cdata[i], 1);
  59. } else {
  60. if (s_tx_mode == ESP_LINE_ENDINGS_CRLF || s_tx_mode == ESP_LINE_ENDINGS_CR) {
  61. char cr = '\r';
  62. esp_usb_console_write_buf(&cr, 1);
  63. }
  64. if (s_tx_mode == ESP_LINE_ENDINGS_CRLF || s_tx_mode == ESP_LINE_ENDINGS_LF) {
  65. char lf = '\n';
  66. esp_usb_console_write_buf(&lf, 1);
  67. }
  68. }
  69. }
  70. _lock_release_recursive(&s_write_lock);
  71. return size;
  72. }
  73. static int cdcacm_fsync(int fd)
  74. {
  75. assert(fd == 0);
  76. _lock_acquire_recursive(&s_write_lock);
  77. ssize_t written = esp_usb_console_flush();
  78. _lock_release_recursive(&s_write_lock);
  79. return (written < 0) ? -1 : 0;
  80. }
  81. static int cdcacm_open(const char *path, int flags, int mode)
  82. {
  83. return 0; // fd 0
  84. }
  85. static int cdcacm_fstat(int fd, struct stat *st)
  86. {
  87. assert(fd == 0);
  88. memset(st, 0, sizeof(*st));
  89. st->st_mode = S_IFCHR;
  90. return 0;
  91. }
  92. static int cdcacm_close(int fd)
  93. {
  94. assert(fd == 0);
  95. return 0;
  96. }
  97. static int s_peek_char = NONE;
  98. /* Helper function which returns a previous character or reads a new one from
  99. * CDC-ACM driver. Previous character can be returned ("pushed back") using
  100. * cdcacm_return_char function. Returns NONE if no character is available. Note
  101. * the cdcacm driver maintains its own RX buffer and a receive call does not
  102. * invoke an USB operation, so there's no penalty to reading data char-by-char.
  103. */
  104. static int cdcacm_read_char(void)
  105. {
  106. /* return character from peek buffer, if it is there */
  107. if (s_peek_char != NONE) {
  108. int c = s_peek_char;
  109. s_peek_char = NONE;
  110. return c;
  111. }
  112. /* Peek buffer is empty; try to read from cdcacm driver. */
  113. uint8_t c;
  114. ssize_t read = esp_usb_console_read_buf((char *) &c, 1);
  115. if (read <= 0) {
  116. return NONE;
  117. } else {
  118. return c;
  119. }
  120. }
  121. static bool cdcacm_data_in_buffer(void)
  122. {
  123. if (s_peek_char != NONE) {
  124. return true;
  125. }
  126. if (esp_usb_console_read_available()) {
  127. return true;
  128. }
  129. return false;
  130. }
  131. /* Push back a character; it will be returned by next call to cdcacm_read_char */
  132. static void cdcacm_return_char(int c)
  133. {
  134. assert(s_peek_char == NONE);
  135. s_peek_char = c;
  136. }
  137. static ssize_t cdcacm_read(int fd, void *data, size_t size)
  138. {
  139. assert(fd == 0);
  140. char *data_c = (char *) data;
  141. ssize_t received = 0;
  142. _lock_acquire_recursive(&s_read_lock);
  143. while (!cdcacm_data_in_buffer()) {
  144. if (!s_blocking) {
  145. errno = EWOULDBLOCK;
  146. _lock_release_recursive(&s_read_lock);
  147. return -1;
  148. }
  149. xSemaphoreTake(s_rx_semaphore, portMAX_DELAY);
  150. }
  151. if (s_rx_mode == ESP_LINE_ENDINGS_CR || s_rx_mode == ESP_LINE_ENDINGS_LF) {
  152. /* This is easy. Just receive, and if needed replace \r by \n. */
  153. received = esp_usb_console_read_buf(data_c, size);
  154. if (s_rx_mode == ESP_LINE_ENDINGS_CR) {
  155. /* Change CRs to newlines */
  156. for (ssize_t i = 0; i < received; i++) {
  157. if (data_c[i] == '\r') {
  158. data_c[i] = '\n';
  159. }
  160. }
  161. }
  162. } else {
  163. while (received < size) {
  164. int c = cdcacm_read_char();
  165. if (c == '\r') {
  166. /* look ahead */
  167. int c2 = cdcacm_read_char();
  168. if (c2 == NONE) {
  169. /* could not look ahead, put the current character back */
  170. cdcacm_return_char(c);
  171. break;
  172. }
  173. if (c2 == '\n') {
  174. /* this was \r\n sequence. discard \r, return \n */
  175. c = '\n';
  176. } else {
  177. /* \r followed by something else. put the second char back,
  178. * it will be processed on next iteration. return \r now.
  179. */
  180. cdcacm_return_char(c2);
  181. }
  182. } else if (c == NONE) {
  183. break;
  184. }
  185. data_c[received++] = (char) c;
  186. if (c == '\n') {
  187. break;
  188. }
  189. }
  190. }
  191. _lock_release_recursive(&s_read_lock);
  192. if (received > 0) {
  193. return received;
  194. }
  195. errno = EWOULDBLOCK;
  196. return -1;
  197. }
  198. /* Non-static, to be able to place into IRAM by ldgen */
  199. void cdcacm_rx_cb(void* arg)
  200. {
  201. assert(s_blocking);
  202. xSemaphoreGive(s_rx_semaphore);
  203. }
  204. /* Non-static, to be able to place into IRAM by ldgen */
  205. void cdcacm_tx_cb(void* arg)
  206. {
  207. assert(s_blocking);
  208. xSemaphoreGive(s_tx_semaphore);
  209. }
  210. static int cdcacm_enable_blocking(void)
  211. {
  212. s_rx_semaphore = xSemaphoreCreateBinary();
  213. if (!s_rx_semaphore) {
  214. errno = ENOMEM;
  215. goto fail;
  216. }
  217. s_tx_semaphore = xSemaphoreCreateBinary();
  218. if (!s_tx_semaphore) {
  219. errno = ENOMEM;
  220. goto fail;
  221. }
  222. esp_err_t err = esp_usb_console_set_cb(&cdcacm_rx_cb, &cdcacm_tx_cb, NULL);
  223. if (err != ESP_OK) {
  224. errno = ENODEV;
  225. goto fail;
  226. }
  227. s_blocking = true;
  228. return 0;
  229. fail:
  230. if (s_rx_semaphore) {
  231. vSemaphoreDelete(s_rx_semaphore);
  232. s_rx_semaphore = NULL;
  233. }
  234. if (s_tx_semaphore) {
  235. vSemaphoreDelete(s_tx_semaphore);
  236. s_tx_semaphore = NULL;
  237. }
  238. return -1;
  239. }
  240. static int cdcacm_disable_blocking(void)
  241. {
  242. esp_usb_console_set_cb(NULL, NULL, NULL); /* ignore any errors */
  243. vSemaphoreDelete(s_rx_semaphore);
  244. s_rx_semaphore = NULL;
  245. vSemaphoreDelete(s_tx_semaphore);
  246. s_tx_semaphore = NULL;
  247. s_blocking = false;
  248. return 0;
  249. }
  250. static int cdcacm_fcntl(int fd, int cmd, int arg)
  251. {
  252. assert(fd == 0);
  253. int result;
  254. if (cmd == F_GETFL) {
  255. result = 0;
  256. if (!s_blocking) {
  257. result |= O_NONBLOCK;
  258. }
  259. } else if (cmd == F_SETFL) {
  260. bool blocking = (arg & O_NONBLOCK) == 0;
  261. result = 0;
  262. if (blocking && !s_blocking) {
  263. result = cdcacm_enable_blocking();
  264. } else if (!blocking && s_blocking) {
  265. result = cdcacm_disable_blocking();
  266. }
  267. } else {
  268. /* unsupported operation */
  269. result = -1;
  270. errno = ENOSYS;
  271. }
  272. return result;
  273. }
  274. void esp_vfs_dev_cdcacm_set_tx_line_endings(esp_line_endings_t mode)
  275. {
  276. s_tx_mode = mode;
  277. }
  278. void esp_vfs_dev_cdcacm_set_rx_line_endings(esp_line_endings_t mode)
  279. {
  280. s_rx_mode = mode;
  281. }
  282. esp_err_t esp_vfs_dev_cdcacm_register(void)
  283. {
  284. const esp_vfs_t vfs = {
  285. .flags = ESP_VFS_FLAG_DEFAULT,
  286. .write = &cdcacm_write,
  287. .open = &cdcacm_open,
  288. .fstat = &cdcacm_fstat,
  289. .close = &cdcacm_close,
  290. .read = &cdcacm_read,
  291. .fcntl = &cdcacm_fcntl,
  292. .fsync = &cdcacm_fsync
  293. };
  294. return esp_vfs_register("/dev/cdcacm", &vfs, NULL);
  295. }