1111#include " driver/usb_serial_jtag_vfs.h"
1212#include " esp_err.h"
1313#include " esp_system.h"
14+ #include < fcntl.h>
1415
16+ #include " esp_vfs_cdcacm.h"
1517#include " esp_vfs_dev.h"
1618#include " esp_vfs_usb_serial_jtag.h"
1719
1820#include " line_input.hpp"
1921
20- #ifdef CONFIG_ESP_CONSOLE_USB_CDC
21- #error The cli component is currently incompatible with CONFIG ESP_CONSOLE_USB_CDC console.
22- #endif // CONFIG_ESP_CONSOLE_USB_CDC
23-
2422#ifndef STRINGIFY
2523#define STRINGIFY (s ) STRINGIFY2(s)
2624#define STRINGIFY2 (s ) #s
@@ -38,9 +36,9 @@ namespace espp {
3836 *
3937 * @note You should call configure_stdin_stdout() before creating a Cli object
4038 * to ensure that std::cin works as needed. If you do not want to use the
41- * Cli over the ESP CONSOLE (e.g. the ESP's UART, USB Serial/JTAG) and
42- * instead want to run it over a different UART port, VFS, or some other
43- * configuration, then you should call one of
39+ * Cli over the ESP CONSOLE (e.g. the ESP's UART, USB Serial/JTAG, USB
40+ * CDC) and instead want to run it over a different UART port, VFS, or
41+ * some other configuration, then you should call one of
4442 * - configure_stdin_stdout_uart()
4543 * - configure_stdin_stdout_vfs()
4644 * - configure_stdin_stdout_custom()
@@ -58,6 +56,7 @@ class Cli : private cli::CliSession {
5856 * compiled to use. This will only work if the ESP_CONSOLE was
5957 * configured to use one of the following:
6058 * - CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
59+ * - CONFIG_ESP_CONSOLE_USB_CDC
6160 * - CONFIG_ESP_CONSOLE_UART
6261 *
6362 * If you want to use a different console, you should use one of the
@@ -77,6 +76,8 @@ class Cli : private cli::CliSession {
7776
7877#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
7978 configure_stdin_stdout_usb_serial_jtag ();
79+ #elif CONFIG_ESP_CONSOLE_USB_CDC
80+ configure_stdin_stdout_usb_cdc ();
8081#elif CONFIG_ESP_CONSOLE_UART
8182 configure_stdin_stdout_uart ((uart_port_t )CONFIG_ESP_CONSOLE_UART_NUM,
8283 CONFIG_ESP_CONSOLE_UART_BAUDRATE);
@@ -182,6 +183,47 @@ class Cli : private cli::CliSession {
182183 configured_ = true ;
183184 }
184185
186+ /* *
187+ * @brief Configure the USB CDC driver to support blocking input read, so
188+ * that std::cin (which assumes a blocking read) will function. This
189+ * should be primarily used when you want to use the std::cin/std::getline
190+ * and other std input functions or you want to use the cli library.
191+ */
192+ static void configure_stdin_stdout_usb_cdc (void ) {
193+ if (configured_) {
194+ return ;
195+ }
196+
197+ // drain stdout before reconfiguring it
198+ fflush (stdout);
199+ fsync (fileno (stdout));
200+
201+ const std::string_view dev_name = " /dev/cdcacm" ;
202+
203+ // redirect stdin, stdout, stderr to the USB CDC interface
204+ console_.in = freopen (dev_name.data (), " r" , stdin);
205+ console_.out = freopen (dev_name.data (), " w" , stdout);
206+ console_.err = freopen (dev_name.data (), " w" , stderr);
207+
208+ esp_vfs_dev_cdcacm_register ();
209+
210+ esp_vfs_dev_cdcacm_set_rx_line_endings (ESP_LINE_ENDINGS_CR);
211+ esp_vfs_dev_cdcacm_set_tx_line_endings (ESP_LINE_ENDINGS_CRLF);
212+
213+ // Enable blocking mode on stdin and stdout
214+ fcntl (fileno (stdout), F_SETFL, 0 );
215+ fcntl (fileno (stdin), F_SETFL, 0 );
216+
217+ // Initialize VFS & UART so we can use std::cout/cin
218+ // _IOFBF = full buffering
219+ // _IOLBF = line buffering
220+ // _IONBF = no buffering
221+ // disable buffering on stdin
222+ setvbuf (stdin, nullptr , _IONBF, 0 );
223+
224+ configured_ = true ;
225+ }
226+
185227 /* *
186228 * @brief Configure stdin/stdout to use a custom VFS driver. This should be
187229 * used when you have a custom VFS driver that you want to use for
@@ -218,8 +260,6 @@ class Cli : private cli::CliSession {
218260 // Register the USB CDC interface
219261 [[maybe_unused]] auto err = esp_vfs_register (dev_name.data (), &vfs, NULL );
220262
221- // TODO: this function is mostly untested, so we should probably add some
222- // error handling here and store the resultant pointers for later use
223263 // redirect stdin, stdout, stderr to the USB CDC interface
224264 console_.in = freopen (dev_name.data (), " r" , stdin);
225265 console_.out = freopen (dev_name.data (), " w" , stdout);
0 commit comments