Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/host/pico_stdio/include/pico/stdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 14 additions & 1 deletion src/rp2_common/pico_stdio/include/pico/stdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
*/
Expand All @@ -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
Expand Down
31 changes: 24 additions & 7 deletions src/rp2_common/pico_stdio/stdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -89,21 +92,22 @@ 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
return false;
#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) {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down
24 changes: 23 additions & 1 deletion src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess a common usage-idiom (for example-code that wants to be switchable between UART and USB-CDC using just the CMakeLists.txt mechanism) would be to wrap this inside a #if LIB_PICO_STDIO_USB ? Would that code need to also explicitly include stdio_usb.h inside a #if LIB_PICO_STDIO_USB too?
Or have I grabbed the totally wrong end of the stick? 🙃

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you would do exact all that... however the other #define stuff avoids you having to mess with stuff. I did not want to try to shoe-horn connection waiting into the abstraction for stdio drivers atm especially since the UART one is not capable of waiting even though you could detect a UART connection with more pins

#ifdef __cplusplus
}
#endif
Expand Down
20 changes: 20 additions & 0 deletions src/rp2_common/pico_stdio_usb/stdio_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down