diff --git a/src/host/pico_stdio/include/pico/stdio.h b/src/host/pico_stdio/include/pico/stdio.h index 5431853..de0bde1 100644 --- a/src/host/pico_stdio/include/pico/stdio.h +++ b/src/host/pico_stdio/include/pico/stdio.h @@ -16,6 +16,9 @@ void stdio_uart_init(); static inline void stdio_init_all() { stdio_uart_init(); } static inline void stdio_filter_driver(stdio_driver_t *driver) {} static inline void stdio_set_translate_crlf(stdio_driver_t *driver, bool enabled) {} +static inline bool stdio_usb_connected(void) { return true; } int getchar_timeout_us(uint32_t timeout_us); +#define puts_raw puts +#define putchar_raw putchar #endif diff --git a/src/rp2_common/pico_stdio/include/pico/stdio.h b/src/rp2_common/pico_stdio/include/pico/stdio.h index 9f01ae9..e44c01d 100644 --- a/src/rp2_common/pico_stdio/include/pico/stdio.h +++ b/src/rp2_common/pico_stdio/include/pico/stdio.h @@ -49,6 +49,9 @@ typedef struct stdio_driver stdio_driver_t; * Call this method once you have set up your clocks to enable the stdio support for UART, USB * and semihosting based on the presence of the respective libraries in the binary. * + * When stdio_usb is configured, this method can be optionally made to block, waiting for a connection + * via the variables specified in \ref stdio_usb_init (i.e. \ref PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS) + * * \see stdio_uart, stdio_usb, stdio_semihosting */ void stdio_init_all(void); @@ -74,7 +77,7 @@ int getchar_timeout_us(uint32_t timeout_us); /*! \brief Adds or removes a driver from the list of active drivers used for input/output * \ingroup pico_stdio * - * \note this method should always be called on an initialized driver + * \note this method should always be called on an initialized driver and is not re-entrant * \param driver the driver * \param enabled true to add, false to remove */ @@ -100,6 +103,16 @@ void stdio_filter_driver(stdio_driver_t *driver); */ void stdio_set_translate_crlf(stdio_driver_t *driver, bool translate); +/*! \brief putchar variant that skips any CR/LF conversion if enabled + * \ingroup pico_stdio + */ +int putchar_raw(int c); + +/*! \brief puts variant that skips any CR/LF conversion if enabled + * \ingroup pico_stdio + */ +int puts_raw(const char *s); + #ifdef __cplusplus } #endif diff --git a/src/rp2_common/pico_stdio/stdio.c b/src/rp2_common/pico_stdio/stdio.c index bf10ad6..8dd7257 100644 --- a/src/rp2_common/pico_stdio/stdio.c +++ b/src/rp2_common/pico_stdio/stdio.c @@ -59,6 +59,9 @@ static bool stdout_serialize_begin(void) { static void stdout_serialize_end(void) { } #endif +static void stdio_out_chars_no_crlf(stdio_driver_t *driver, const char *s, int len) { + driver->out_chars(s, len); +} static void stdio_out_chars_crlf(stdio_driver_t *driver, const char *s, int len) { #if PICO_STDIO_ENABLE_CRLF_SUPPORT @@ -89,7 +92,7 @@ static void stdio_out_chars_crlf(stdio_driver_t *driver, const char *s, int len) #endif } -static bool stdio_put_string(const char *s, int len, bool newline) { +static bool stdio_put_string(const char *s, int len, bool newline, bool no_cr) { bool serialized = stdout_serialize_begin(); if (!serialized) { #if PICO_STDIO_IGNORE_NESTED_STDOUT @@ -97,13 +100,14 @@ static bool stdio_put_string(const char *s, int len, bool newline) { #endif } if (len == -1) len = (int)strlen(s); + void (*out_func)(stdio_driver_t *, const char *, int) = no_cr ? stdio_out_chars_no_crlf : stdio_out_chars_crlf; for (stdio_driver_t *driver = drivers; driver; driver = driver->next) { if (!driver->out_chars) continue; if (filter && filter != driver) continue; - stdio_out_chars_crlf(driver, s, len); + out_func(driver, s, len); if (newline) { const char c = '\n'; - stdio_out_chars_crlf(driver, &c, 1); + out_func(driver, &c, 1); } } if (serialized) { @@ -134,13 +138,26 @@ static int stdio_get_until(char *buf, int len, absolute_time_t until) { int WRAPPER_FUNC(putchar)(int c) { char cc = (char)c; - stdio_put_string(&cc, 1, false); + stdio_put_string(&cc, 1, false, false); return c; } int WRAPPER_FUNC(puts)(const char *s) { int len = (int)strlen(s); - stdio_put_string(s, len, true); + stdio_put_string(s, len, true, false); + stdio_flush(); + return len; +} + +int putchar_raw(int c) { + char cc = (char)c; + stdio_put_string(&cc, 1, false, true); + return c; +} + +int puts_raw(const char *s) { + int len = (int)strlen(s); + stdio_put_string(s, len, true, true); stdio_flush(); return len; } @@ -154,7 +171,7 @@ int _read(int handle, char *buffer, int length) { int _write(int handle, char *buffer, int length) { if (handle == 1) { - stdio_put_string(buffer, length, false); + stdio_put_string(buffer, length, false, false); return length; } return -1; @@ -243,7 +260,7 @@ int __printflike(1, 0) WRAPPER_FUNC(printf)(const char* format, ...) return ret; } -void stdio_init_all() { +void stdio_init_all(void) { // todo add explicit custom, or registered although you can call stdio_enable_driver explicitly anyway // These are well known ones #if LIB_PICO_STDIO_UART diff --git a/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h b/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h index 6a1effa..db63142 100644 --- a/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h +++ b/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h @@ -54,6 +54,16 @@ #define PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE 1200 #endif +// PICO_CONFIG: PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS, Maximum number of milliseconds to wait during initialization for a CDC connection from the host (negative means indefinite) before returning, default=0, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS +#define PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS 0 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS, Number of milliseconds to wait during initialization after a host CDC connection is detected before returning (some host terminals seem to sometimes lose transmissions sent right after connection), default=50, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS +#define PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS 50 +#endif + // PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED, Optionally define a pin to use as bootloader activity LED when BOOTSEL mode is entered via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=29, group=pico_stdio_usb // PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED, Whether the pin specified by PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED is fixed or can be modified by picotool over the VENDOR USB interface, type=bool, default=0, group=pico_stdio_usb @@ -94,10 +104,22 @@ extern "C" { extern stdio_driver_t stdio_usb; /*! \brief Explicitly initialize USB stdio and add it to the current set of stdin drivers - * \ingroup pico_stdio_uart + * \ingroup pico_stdio_usb + * + * \ref PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS can be set to cause this method to wait for a CDC connection + * from the host before returning, which is useful if you don't want any initial stdout output to be discarded + * before the connection is established. + * + * \return true if the USB CDC was initialized, false if an error occurred */ bool stdio_usb_init(void); +/*! \brief Check if there is an active stdio CDC connection to a host + * \ingroup pico_stdio_usb + * + * \return true if stdio is connected over CDC + */ +bool stdio_usb_connected(void); #ifdef __cplusplus } #endif diff --git a/src/rp2_common/pico_stdio_usb/stdio_usb.c b/src/rp2_common/pico_stdio_usb/stdio_usb.c index 2cca2c2..42ea4df 100644 --- a/src/rp2_common/pico_stdio_usb/stdio_usb.c +++ b/src/rp2_common/pico_stdio_usb/stdio_usb.c @@ -103,9 +103,29 @@ bool stdio_usb_init(void) { bool rc = add_alarm_in_us(PICO_STDIO_USB_TASK_INTERVAL_US, timer_task, NULL, true); if (rc) { stdio_set_driver_enabled(&stdio_usb, true); +#if PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS +#if PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS > 0 + absolute_time_t until = make_timeout_time_ms(PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS); +#else + absolute_time_t until = at_the_end_of_time; +#endif + do { + if (stdio_usb_connected()) { +#if PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS != 0 + sleep_ms(PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS); +#endif + break; + } + sleep_ms(10); + } while (!time_reached(until)); +#endif } return rc; } + +bool stdio_usb_connected(void) { + return tud_cdc_connected(); +} #else #include "pico/stdio_usb.h" #warning stdio USB was configured, but is being disabled as TinyUSB is explicitly linked