usb_console.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /*
  2. * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <assert.h>
  9. #include <sys/param.h>
  10. #include "sdkconfig.h"
  11. #include "freertos/FreeRTOS.h"
  12. #include "freertos/task.h"
  13. #include "freertos/semphr.h"
  14. #include "esp_system.h"
  15. #include "esp_log.h"
  16. #include "esp_timer.h"
  17. #include "esp_check.h"
  18. #include "esp_intr_alloc.h"
  19. #include "esp_private/usb_console.h"
  20. #include "esp_private/system_internal.h"
  21. #include "esp_private/startup_internal.h"
  22. #include "soc/periph_defs.h"
  23. #include "soc/rtc_cntl_reg.h"
  24. #include "soc/usb_struct.h"
  25. #include "soc/usb_reg.h"
  26. #include "hal/soc_hal.h"
  27. #include "esp_rom_uart.h"
  28. #include "esp_rom_sys.h"
  29. #include "esp_rom_caps.h"
  30. #ifdef CONFIG_IDF_TARGET_ESP32S2
  31. #include "esp32s2/rom/usb/usb_dc.h"
  32. #include "esp32s2/rom/usb/cdc_acm.h"
  33. #include "esp32s2/rom/usb/usb_dfu.h"
  34. #include "esp32s2/rom/usb/usb_device.h"
  35. #include "esp32s2/rom/usb/usb_os_glue.h"
  36. #include "esp32s2/rom/usb/usb_persist.h"
  37. #include "esp32s2/rom/usb/chip_usb_dw_wrapper.h"
  38. #elif CONFIG_IDF_TARGET_ESP32S3
  39. #include "esp32s3/rom/usb/usb_dc.h"
  40. #include "esp32s3/rom/usb/cdc_acm.h"
  41. #include "esp32s3/rom/usb/usb_dfu.h"
  42. #include "esp32s3/rom/usb/usb_device.h"
  43. #include "esp32s3/rom/usb/usb_os_glue.h"
  44. #include "esp32s3/rom/usb/usb_persist.h"
  45. #include "esp32s3/rom/usb/chip_usb_dw_wrapper.h"
  46. #endif
  47. #define CDC_WORK_BUF_SIZE (ESP_ROM_CDC_ACM_WORK_BUF_MIN + CONFIG_ESP_CONSOLE_USB_CDC_RX_BUF_SIZE)
  48. typedef enum {
  49. REBOOT_NONE,
  50. REBOOT_NORMAL,
  51. REBOOT_BOOTLOADER,
  52. REBOOT_BOOTLOADER_DFU,
  53. } reboot_type_t;
  54. static reboot_type_t s_queue_reboot = REBOOT_NONE;
  55. static int s_prev_rts_state;
  56. static intr_handle_t s_usb_int_handle;
  57. static cdc_acm_device *s_cdc_acm_device;
  58. static char s_usb_tx_buf[ACM_BYTES_PER_TX];
  59. static size_t s_usb_tx_buf_pos;
  60. static uint8_t cdcmem[CDC_WORK_BUF_SIZE];
  61. static esp_usb_console_cb_t s_rx_cb;
  62. static esp_usb_console_cb_t s_tx_cb;
  63. static void *s_cb_arg;
  64. static esp_timer_handle_t s_restart_timer;
  65. static const char* TAG = "usb_console";
  66. /* This lock is used for two purposes:
  67. * - To protect functions which write something to USB, e.g. esp_usb_console_write_buf.
  68. * This is necessary since these functions may be called by esp_rom_printf, so the calls
  69. * may preempt each other or happen concurrently.
  70. * (The calls coming from regular 'printf', i.e. via VFS layer, are already protected
  71. * by a mutex in the VFS driver.)
  72. * - To implement "osglue" functions of the USB stack. These normally require interrupts
  73. * to be disabled. However on multi-core chips a critical section is necessary.
  74. */
  75. static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED;
  76. #ifdef CONFIG_ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF
  77. void esp_usb_console_write_char(char c);
  78. #define ISR_FLAG ESP_INTR_FLAG_IRAM
  79. #else
  80. #define ISR_FLAG 0
  81. #endif // CONFIG_ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF
  82. /* Optional write lock routines; used only if esp_rom_printf output via CDC is enabled */
  83. static inline void write_lock_acquire(void);
  84. static inline void write_lock_release(void);
  85. /* Other forward declarations */
  86. void esp_usb_console_before_restart(void);
  87. /* Called by ROM to disable the interrupts
  88. * Non-static to allow placement into IRAM by ldgen.
  89. */
  90. void esp_usb_console_osglue_dis_int(void)
  91. {
  92. portENTER_CRITICAL_SAFE(&s_lock);
  93. }
  94. /* Called by ROM to enable the interrupts
  95. * Non-static to allow placement into IRAM by ldgen.
  96. */
  97. void esp_usb_console_osglue_ena_int(void)
  98. {
  99. portEXIT_CRITICAL_SAFE(&s_lock);
  100. }
  101. /* Delay function called by ROM USB driver.
  102. * Non-static to allow placement into IRAM by ldgen.
  103. */
  104. int esp_usb_console_osglue_wait_proc(int delay_us)
  105. {
  106. if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING ||
  107. !xPortCanYield()) {
  108. esp_rom_delay_us(delay_us);
  109. return delay_us;
  110. }
  111. if (delay_us == 0) {
  112. /* We should effectively yield */
  113. vPortYield();
  114. return 1;
  115. } else {
  116. /* Just delay */
  117. int ticks = MAX(delay_us / (portTICK_PERIOD_MS * 1000), 1);
  118. vTaskDelay(ticks);
  119. return ticks * portTICK_PERIOD_MS * 1000;
  120. }
  121. }
  122. /* Called by ROM CDC ACM driver from interrupt context./
  123. * Non-static to allow placement into IRAM by ldgen.
  124. */
  125. void esp_usb_console_cdc_acm_cb(cdc_acm_device *dev, int status)
  126. {
  127. if (status == USB_DC_RESET || status == USB_DC_CONNECTED) {
  128. s_prev_rts_state = 0;
  129. } else if (status == ACM_STATUS_LINESTATE_CHANGED) {
  130. uint32_t rts, dtr;
  131. cdc_acm_line_ctrl_get(dev, LINE_CTRL_RTS, &rts);
  132. cdc_acm_line_ctrl_get(dev, LINE_CTRL_DTR, &dtr);
  133. if (!rts && s_prev_rts_state) {
  134. if (dtr) {
  135. s_queue_reboot = REBOOT_BOOTLOADER;
  136. } else {
  137. s_queue_reboot = REBOOT_NORMAL;
  138. }
  139. }
  140. s_prev_rts_state = rts;
  141. } else if (status == ACM_STATUS_RX && s_rx_cb) {
  142. (*s_rx_cb)(s_cb_arg);
  143. } else if (status == ACM_STATUS_TX && s_tx_cb) {
  144. (*s_tx_cb)(s_cb_arg);
  145. }
  146. }
  147. /* Non-static to allow placement into IRAM by ldgen. */
  148. void esp_usb_console_dfu_detach_cb(int timeout)
  149. {
  150. s_queue_reboot = REBOOT_BOOTLOADER_DFU;
  151. }
  152. /* USB interrupt handler, forward the call to the ROM driver.
  153. * Non-static to allow placement into IRAM by ldgen.
  154. */
  155. void esp_usb_console_interrupt(void *arg)
  156. {
  157. usb_dc_check_poll_for_interrupts();
  158. /* Restart can be requested from esp_usb_console_cdc_acm_cb or esp_usb_console_dfu_detach_cb */
  159. if (s_queue_reboot != REBOOT_NONE) {
  160. /* We can't call esp_restart here directly, since this function is called from an ISR.
  161. * Instead, start an esp_timer and call esp_restart from the callback.
  162. */
  163. esp_err_t err = ESP_FAIL;
  164. if (s_restart_timer) {
  165. /* In case the timer is already running, stop it. No error check since this will fail if
  166. * the timer is not running.
  167. */
  168. esp_timer_stop(s_restart_timer);
  169. /* Start the timer again. 50ms seems to be not too long for the user to notice, but
  170. * enough for the USB console output to be flushed.
  171. */
  172. const int restart_timeout_us = 50 * 1000;
  173. err = esp_timer_start_once(s_restart_timer, restart_timeout_us);
  174. }
  175. if (err != ESP_OK) {
  176. /* Can't schedule a restart for some reason? Call the "no-OS" restart function directly. */
  177. esp_usb_console_before_restart();
  178. esp_restart_noos();
  179. }
  180. }
  181. }
  182. /* Called as esp_timer callback when the restart timeout expires.
  183. * Non-static to allow placement into IRAM by ldgen.
  184. */
  185. void esp_usb_console_on_restart_timeout(void *arg)
  186. {
  187. esp_restart();
  188. }
  189. /* Call the USB interrupt handler while any interrupts are pending,
  190. * but not more than a few times.
  191. * Non-static to allow placement into IRAM by ldgen.
  192. */
  193. void esp_usb_console_poll_interrupts(void)
  194. {
  195. const int max_poll_count = 10;
  196. for (int i = 0; (USB0.gintsts & USB0.gintmsk) != 0 && i < max_poll_count; i++) {
  197. usb_dc_check_poll_for_interrupts();
  198. }
  199. }
  200. /* This function gets registered as a restart handler.
  201. * Prepares USB peripheral for restart and sets up persistence.
  202. * Non-static to allow placement into IRAM by ldgen.
  203. */
  204. void esp_usb_console_before_restart(void)
  205. {
  206. esp_usb_console_poll_interrupts();
  207. usb_dc_prepare_persist();
  208. if (s_queue_reboot == REBOOT_BOOTLOADER) {
  209. chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
  210. REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
  211. } else if (s_queue_reboot == REBOOT_BOOTLOADER_DFU) {
  212. chip_usb_set_persist_flags(USBDC_BOOT_DFU);
  213. REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
  214. } else {
  215. chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
  216. esp_usb_console_poll_interrupts();
  217. }
  218. }
  219. /* Reset some static state in ROM, which survives when going from the
  220. * 2nd stage bootloader into the app. This cleans some variables which
  221. * indicates that the driver is already initialized, allowing us to
  222. * initialize it again, in the app.
  223. */
  224. static void esp_usb_console_rom_cleanup(void)
  225. {
  226. usb_dev_deinit();
  227. usb_dw_ctrl_deinit();
  228. uart_acm_dev = NULL;
  229. }
  230. esp_err_t esp_usb_console_init(void)
  231. {
  232. esp_err_t err;
  233. err = esp_register_shutdown_handler(esp_usb_console_before_restart);
  234. if (err != ESP_OK) {
  235. return err;
  236. }
  237. esp_usb_console_rom_cleanup();
  238. /* Install OS hooks */
  239. rom_usb_osglue.int_dis_proc = esp_usb_console_osglue_dis_int;
  240. rom_usb_osglue.int_ena_proc = esp_usb_console_osglue_ena_int;
  241. rom_usb_osglue.wait_proc = esp_usb_console_osglue_wait_proc;
  242. /* Install interrupt.
  243. * In case of ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF:
  244. * Note that this the interrupt handler has to be placed into IRAM because
  245. * the interrupt handler can also be called in polling mode, when
  246. * interrupts are disabled, and a write to USB is performed with cache disabled.
  247. * Since the handler function is in IRAM, we can register the interrupt as IRAM capable.
  248. * It is not because we actually need the interrupt to work with cache disabled!
  249. */
  250. err = esp_intr_alloc(ETS_USB_INTR_SOURCE, ISR_FLAG | ESP_INTR_FLAG_INTRDISABLED,
  251. esp_usb_console_interrupt, NULL, &s_usb_int_handle);
  252. if (err != ESP_OK) {
  253. esp_unregister_shutdown_handler(esp_usb_console_before_restart);
  254. return err;
  255. }
  256. /* Initialize USB / CDC */
  257. s_cdc_acm_device = cdc_acm_init(cdcmem, CDC_WORK_BUF_SIZE);
  258. usb_dc_check_poll_for_interrupts();
  259. /* Set callback for handling DTR/RTS lines and TX/RX events */
  260. cdc_acm_irq_callback_set(s_cdc_acm_device, esp_usb_console_cdc_acm_cb);
  261. cdc_acm_irq_state_enable(s_cdc_acm_device);
  262. /* Set callback for handling DFU detach */
  263. usb_dfu_set_detach_cb(esp_usb_console_dfu_detach_cb);
  264. /* Enable interrupts on USB peripheral side */
  265. USB0.gahbcfg |= USB_GLBLLNTRMSK_M;
  266. /* Enable the interrupt handler */
  267. esp_intr_enable(s_usb_int_handle);
  268. #ifdef CONFIG_ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF
  269. /* Install esp_rom_printf handler */
  270. esp_rom_uart_set_as_console(ESP_ROM_USB_OTG_NUM);
  271. esp_rom_install_channel_putc(1, &esp_usb_console_write_char);
  272. #endif // CONFIG_ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF
  273. return ESP_OK;
  274. }
  275. /* This function runs as part of the startup code to initialize the restart timer.
  276. * This is not done as part of esp_usb_console_init since that function is called
  277. * too early, before esp_timer is fully initialized.
  278. * This gets called a bit later in the process when we can already register a timer.
  279. */
  280. ESP_SYSTEM_INIT_FN(esp_usb_console_init_restart_timer, BIT(0), 220)
  281. {
  282. esp_timer_create_args_t timer_create_args = {
  283. .callback = &esp_usb_console_on_restart_timeout,
  284. .name = "usb_console_restart"
  285. };
  286. ESP_RETURN_ON_ERROR(esp_timer_create(&timer_create_args, &s_restart_timer), TAG, "failed to create the restart timer");
  287. return ESP_OK;
  288. }
  289. /* Non-static to allow placement into IRAM by ldgen.
  290. * Must be called with the write lock held.
  291. */
  292. ssize_t esp_usb_console_flush_internal(size_t last_write_size)
  293. {
  294. if (s_usb_tx_buf_pos == 0) {
  295. return 0;
  296. }
  297. assert(s_usb_tx_buf_pos >= last_write_size);
  298. ssize_t ret;
  299. size_t tx_buf_pos_before = s_usb_tx_buf_pos - last_write_size;
  300. size_t sent = cdc_acm_fifo_fill(s_cdc_acm_device, (const uint8_t*) s_usb_tx_buf, s_usb_tx_buf_pos);
  301. if (sent == last_write_size) {
  302. /* everything was sent */
  303. ret = last_write_size;
  304. s_usb_tx_buf_pos = 0;
  305. } else if (sent == 0) {
  306. /* nothing was sent, roll back to the original state */
  307. ret = 0;
  308. s_usb_tx_buf_pos = tx_buf_pos_before;
  309. } else {
  310. /* Some data was sent, but not all of the buffer.
  311. * We can still tell the caller that all the new data
  312. * was "sent" since it is in the buffer now.
  313. */
  314. ret = last_write_size;
  315. memmove(s_usb_tx_buf, s_usb_tx_buf + sent, s_usb_tx_buf_pos - sent);
  316. s_usb_tx_buf_pos = s_usb_tx_buf_pos - sent;
  317. }
  318. return ret;
  319. }
  320. ssize_t esp_usb_console_flush(void)
  321. {
  322. if (s_cdc_acm_device == NULL) {
  323. return -1;
  324. }
  325. write_lock_acquire();
  326. int ret = esp_usb_console_flush_internal(0);
  327. write_lock_release();
  328. return ret;
  329. }
  330. ssize_t esp_usb_console_write_buf(const char* buf, size_t size)
  331. {
  332. if (s_cdc_acm_device == NULL) {
  333. return -1;
  334. }
  335. if (size == 0) {
  336. return 0;
  337. }
  338. write_lock_acquire();
  339. ssize_t tx_buf_available = ACM_BYTES_PER_TX - s_usb_tx_buf_pos;
  340. ssize_t will_write = MIN(size, tx_buf_available);
  341. memcpy(s_usb_tx_buf + s_usb_tx_buf_pos, buf, will_write);
  342. s_usb_tx_buf_pos += will_write;
  343. ssize_t ret;
  344. if (s_usb_tx_buf_pos == ACM_BYTES_PER_TX || buf[size - 1] == '\n') {
  345. /* Buffer is full, or a newline is found.
  346. * For binary streams, we probably shouldn't do line buffering,
  347. * but text streams are likely going to be the most common case.
  348. */
  349. ret = esp_usb_console_flush_internal(will_write);
  350. } else {
  351. /* nothing sent out yet, but all the new data is in the buffer now */
  352. ret = will_write;
  353. }
  354. write_lock_release();
  355. return ret;
  356. }
  357. ssize_t esp_usb_console_read_buf(char *buf, size_t buf_size)
  358. {
  359. if (s_cdc_acm_device == NULL) {
  360. return -1;
  361. }
  362. if (esp_usb_console_available_for_read() == 0) {
  363. return 0;
  364. }
  365. int bytes_read = cdc_acm_fifo_read(s_cdc_acm_device, (uint8_t*) buf, buf_size);
  366. return bytes_read;
  367. }
  368. esp_err_t esp_usb_console_set_cb(esp_usb_console_cb_t rx_cb, esp_usb_console_cb_t tx_cb, void *arg)
  369. {
  370. if (s_cdc_acm_device == NULL) {
  371. return ESP_ERR_INVALID_STATE;
  372. }
  373. s_rx_cb = rx_cb;
  374. if (s_rx_cb) {
  375. cdc_acm_irq_rx_enable(s_cdc_acm_device);
  376. } else {
  377. cdc_acm_irq_rx_disable(s_cdc_acm_device);
  378. }
  379. s_tx_cb = tx_cb;
  380. if (s_tx_cb) {
  381. cdc_acm_irq_tx_enable(s_cdc_acm_device);
  382. } else {
  383. cdc_acm_irq_tx_disable(s_cdc_acm_device);
  384. }
  385. s_cb_arg = arg;
  386. return ESP_OK;
  387. }
  388. ssize_t esp_usb_console_available_for_read(void)
  389. {
  390. if (s_cdc_acm_device == NULL) {
  391. return -1;
  392. }
  393. return cdc_acm_rx_fifo_cnt(s_cdc_acm_device);
  394. }
  395. bool esp_usb_console_write_available(void)
  396. {
  397. if (s_cdc_acm_device == NULL) {
  398. return false;
  399. }
  400. return cdc_acm_irq_tx_ready(s_cdc_acm_device) != 0;
  401. }
  402. #ifdef CONFIG_ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF
  403. /* Used as an output function by esp_rom_printf.
  404. * The LF->CRLF replacement logic replicates the one in esp_rom_uart_putc.
  405. * Not static to allow placement into IRAM by ldgen.
  406. */
  407. void esp_usb_console_write_char(char c)
  408. {
  409. char cr = '\r';
  410. char lf = '\n';
  411. if (c == lf) {
  412. esp_usb_console_write_buf(&cr, 1);
  413. esp_usb_console_write_buf(&lf, 1);
  414. } else if (c == '\r') {
  415. } else {
  416. esp_usb_console_write_buf(&c, 1);
  417. }
  418. }
  419. static inline void write_lock_acquire(void)
  420. {
  421. portENTER_CRITICAL_SAFE(&s_lock);
  422. }
  423. static inline void write_lock_release(void)
  424. {
  425. portEXIT_CRITICAL_SAFE(&s_lock);
  426. }
  427. #else // CONFIG_ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF
  428. static inline void write_lock_acquire(void)
  429. {
  430. }
  431. static inline void write_lock_release(void)
  432. {
  433. }
  434. #endif // CONFIG_ESP_CONSOLE_USB_CDC_SUPPORT_ETS_PRINTF