13be546dc3
* add stdio_usb_connected() method * add PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS to allow waiting for CDC connection during init(* * add puts_raw and putchar_raw to skip any CR/LF translation
136 lines
4.1 KiB
C
136 lines
4.1 KiB
C
/**
|
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#if !defined(LIB_TINYUSB_HOST) && !defined(LIB_TINYUSB_DEVICE)
|
|
#include "tusb.h"
|
|
|
|
#include "pico/time.h"
|
|
#include "pico/stdio/driver.h"
|
|
#include "pico/binary_info.h"
|
|
#include "pico/mutex.h"
|
|
#include "hardware/irq.h"
|
|
|
|
static_assert(PICO_STDIO_USB_LOW_PRIORITY_IRQ > RTC_IRQ, ""); // note RTC_IRQ is currently the last one
|
|
static mutex_t stdio_usb_mutex;
|
|
|
|
static void low_priority_worker_irq(void) {
|
|
// if the mutex is already owned, then we are in user code
|
|
// in this file which will do a tud_task itself, so we'll just do nothing
|
|
// until the next tick; we won't starve
|
|
if (mutex_try_enter(&stdio_usb_mutex, NULL)) {
|
|
tud_task();
|
|
mutex_exit(&stdio_usb_mutex);
|
|
}
|
|
}
|
|
|
|
static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) {
|
|
irq_set_pending(PICO_STDIO_USB_LOW_PRIORITY_IRQ);
|
|
return PICO_STDIO_USB_TASK_INTERVAL_US;
|
|
}
|
|
|
|
static void stdio_usb_out_chars(const char *buf, int length) {
|
|
static uint64_t last_avail_time;
|
|
uint32_t owner;
|
|
if (!mutex_try_enter(&stdio_usb_mutex, &owner)) {
|
|
if (owner == get_core_num()) return; // would deadlock otherwise
|
|
mutex_enter_blocking(&stdio_usb_mutex);
|
|
}
|
|
if (tud_cdc_connected()) {
|
|
for (int i = 0; i < length;) {
|
|
int n = length - i;
|
|
int avail = tud_cdc_write_available();
|
|
if (n > avail) n = avail;
|
|
if (n) {
|
|
int n2 = tud_cdc_write(buf + i, n);
|
|
tud_task();
|
|
tud_cdc_write_flush();
|
|
i += n2;
|
|
last_avail_time = time_us_64();
|
|
} else {
|
|
tud_task();
|
|
tud_cdc_write_flush();
|
|
if (!tud_cdc_connected() ||
|
|
(!tud_cdc_write_available() && time_us_64() > last_avail_time + PICO_STDIO_USB_STDOUT_TIMEOUT_US)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// reset our timeout
|
|
last_avail_time = 0;
|
|
}
|
|
mutex_exit(&stdio_usb_mutex);
|
|
}
|
|
|
|
int stdio_usb_in_chars(char *buf, int length) {
|
|
uint32_t owner;
|
|
if (!mutex_try_enter(&stdio_usb_mutex, &owner)) {
|
|
if (owner == get_core_num()) return PICO_ERROR_NO_DATA; // would deadlock otherwise
|
|
mutex_enter_blocking(&stdio_usb_mutex);
|
|
}
|
|
int rc = PICO_ERROR_NO_DATA;
|
|
if (tud_cdc_connected() && tud_cdc_available()) {
|
|
int count = tud_cdc_read(buf, length);
|
|
rc = count ? count : PICO_ERROR_NO_DATA;
|
|
}
|
|
mutex_exit(&stdio_usb_mutex);
|
|
return rc;
|
|
}
|
|
|
|
stdio_driver_t stdio_usb = {
|
|
.out_chars = stdio_usb_out_chars,
|
|
.in_chars = stdio_usb_in_chars,
|
|
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
|
|
.crlf_enabled = PICO_STDIO_USB_DEFAULT_CRLF
|
|
#endif
|
|
};
|
|
|
|
bool stdio_usb_init(void) {
|
|
#if !PICO_NO_BI_STDIO_USB
|
|
bi_decl_if_func_used(bi_program_feature("USB stdin / stdout"));
|
|
#endif
|
|
|
|
// initialize TinyUSB
|
|
tusb_init();
|
|
|
|
irq_set_exclusive_handler(PICO_STDIO_USB_LOW_PRIORITY_IRQ, low_priority_worker_irq);
|
|
irq_set_enabled(PICO_STDIO_USB_LOW_PRIORITY_IRQ, true);
|
|
|
|
mutex_init(&stdio_usb_mutex);
|
|
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
|
|
bool stdio_usb_init(void) {
|
|
return false;
|
|
}
|
|
#endif
|