1
6
7
8
9
10
11
12
13
14
15
22
23
24
25
27
28
29
30
31
32
33
34
35
36
37
40
41
42
43
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
92
93
94
95
98
99
100
101
132
133
142
143
144
145
146
147
148
149
150
151
152
153
166
167
168
169
170
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
196
197
198
199
200
201
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
227
228
247
248
249
250
251
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
277
278
279
280
281
282
283
284
292
293
294
299
300
305
/* ... */
#ifndef LIB_TINYUSB_HOST
#include "tusb.h"
#include "pico/stdio_usb.h"
#if (CFG_TUD_ENABLED | TUSB_OPT_DEVICE_ENABLED) && CFG_TUD_CDC
#include "pico/binary_info.h"
#include "pico/time.h"
#include "pico/stdio/driver.h"
#include "pico/mutex.h"
#include "hardware/irq.h"
#include "device/usbd_pvt.h"
6 includes
static mutex_t stdio_usb_mutex;
#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK
static void (*chars_available_callback)(void*);
static void *chars_available_param;/* ... */
#endif
#if !LIB_TINYUSB_DEVICE
static critical_section_t one_shot_timer_crit_sec;
static volatile bool one_shot_timer_pending;
#ifdef PICO_STDIO_USB_LOW_PRIORITY_IRQ
static_assert(PICO_STDIO_USB_LOW_PRIORITY_IRQ >= NUM_IRQS - NUM_USER_IRQS, "");
#define low_priority_irq_num PICO_STDIO_USB_LOW_PRIORITY_IRQ
/* ... */#else
static uint8_t low_priority_irq_num;
#endif
static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) {
int64_t repeat_time;
if (critical_section_is_initialized(&one_shot_timer_crit_sec)) {
critical_section_enter_blocking(&one_shot_timer_crit_sec);
one_shot_timer_pending = false;
critical_section_exit(&one_shot_timer_crit_sec);
repeat_time = 0;
}if (critical_section_is_initialized(&one_shot_timer_crit_sec)) { ... } else {
repeat_time = PICO_STDIO_USB_TASK_INTERVAL_US;
}else { ... }
if (irq_is_enabled(low_priority_irq_num)) {
irq_set_pending(low_priority_irq_num);
return repeat_time;
}if (irq_is_enabled(low_priority_irq_num)) { ... } else {
return 0;
}else { ... }
}{ ... }
static void low_priority_worker_irq(void) {
if (mutex_try_enter(&stdio_usb_mutex, NULL)) {
tud_task();
#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK
uint32_t chars_avail = tud_cdc_available();
#endif
mutex_exit(&stdio_usb_mutex);
#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK
if (chars_avail && chars_available_callback) chars_available_callback(chars_available_param);
#endif
}if (mutex_try_enter(&stdio_usb_mutex, NULL)) { ... } else {
if (critical_section_is_initialized(&one_shot_timer_crit_sec)) {
bool need_timer;
critical_section_enter_blocking(&one_shot_timer_crit_sec);
need_timer = !one_shot_timer_pending;
one_shot_timer_pending = true;
critical_section_exit(&one_shot_timer_crit_sec);
if (need_timer) {
add_alarm_in_us(PICO_STDIO_USB_TASK_INTERVAL_US, timer_task, NULL, true);
}if (need_timer) { ... }
}if (critical_section_is_initialized(&one_shot_timer_crit_sec)) { ... }
}else { ... }
}{ ... }
static void usb_irq(void) {
irq_set_pending(low_priority_irq_num);
}{ ... }
/* ... */#endif
static void stdio_usb_out_chars(const char *buf, int length) {
static uint64_t last_avail_time;
if (!mutex_try_enter_block_until(&stdio_usb_mutex, make_timeout_time_ms(PICO_STDIO_DEADLOCK_TIMEOUT_MS))) {
return;
}if (!mutex_try_enter_block_until(&stdio_usb_mutex, make_timeout_time_ms(PICO_STDIO_DEADLOCK_TIMEOUT_MS))) { ... }
if (stdio_usb_connected()) {
for (int i = 0; i < length;) {
int n = length - i;
int avail = (int) tud_cdc_write_available();
if (n > avail) n = avail;
if (n) {
int n2 = (int) tud_cdc_write(buf + i, (uint32_t)n);
tud_task();
tud_cdc_write_flush();
i += n2;
last_avail_time = time_us_64();
}if (n) { ... } else {
tud_task();
tud_cdc_write_flush();
if (!stdio_usb_connected() ||
(!tud_cdc_write_available() && time_us_64() > last_avail_time + PICO_STDIO_USB_STDOUT_TIMEOUT_US)) {
break;
}if (!stdio_usb_connected() || (!tud_cdc_write_available() && time_us_64() > last_avail_time + PICO_STDIO_USB_STDOUT_TIMEOUT_US)) { ... }
}else { ... }
}for (int i = 0; i < length;) { ... }
}if (stdio_usb_connected()) { ... } else {
last_avail_time = 0;
}else { ... }
mutex_exit(&stdio_usb_mutex);
}{ ... }
static void stdio_usb_out_flush(void) {
if (!mutex_try_enter_block_until(&stdio_usb_mutex, make_timeout_time_ms(PICO_STDIO_DEADLOCK_TIMEOUT_MS))) {
return;
}if (!mutex_try_enter_block_until(&stdio_usb_mutex, make_timeout_time_ms(PICO_STDIO_DEADLOCK_TIMEOUT_MS))) { ... }
do {
tud_task();
...} while (tud_cdc_write_flush());
mutex_exit(&stdio_usb_mutex);
}{ ... }
int stdio_usb_in_chars(char *buf, int length) {
int rc = PICO_ERROR_NO_DATA;
if (stdio_usb_connected() && tud_cdc_available()) {
if (!mutex_try_enter_block_until(&stdio_usb_mutex, make_timeout_time_ms(PICO_STDIO_DEADLOCK_TIMEOUT_MS))) {
return PICO_ERROR_NO_DATA;
}if (!mutex_try_enter_block_until(&stdio_usb_mutex, make_timeout_time_ms(PICO_STDIO_DEADLOCK_TIMEOUT_MS))) { ... }
if (stdio_usb_connected() && tud_cdc_available()) {
int count = (int) tud_cdc_read(buf, (uint32_t) length);
rc = count ? count : PICO_ERROR_NO_DATA;
}if (stdio_usb_connected() && tud_cdc_available()) { ... } else {
tud_task();
}else { ... }
mutex_exit(&stdio_usb_mutex);
}if (stdio_usb_connected() && tud_cdc_available()) { ... }
return rc;
}{ ... }
#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK
void stdio_usb_set_chars_available_callback(void (*fn)(void*), void *param) {
chars_available_callback = fn;
chars_available_param = param;
}{ ... }
/* ... */#endif
stdio_driver_t stdio_usb = {
.out_chars = stdio_usb_out_chars,
.out_flush = stdio_usb_out_flush,
.in_chars = stdio_usb_in_chars,
#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK
.set_chars_available_callback = stdio_usb_set_chars_available_callback,
#endif
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
.crlf_enabled = PICO_STDIO_USB_DEFAULT_CRLF
#endif
...};
bool stdio_usb_init(void) {
if (get_core_num() != alarm_pool_core_num(alarm_pool_get_default())) {
assert(false);
return false;
}if (get_core_num() != alarm_pool_core_num(alarm_pool_get_default())) { ... }
#if !PICO_NO_BI_STDIO_USB
bi_decl_if_func_used(bi_program_feature("USB stdin / stdout"));
#endif
#if !defined(LIB_TINYUSB_DEVICE)
tusb_init();/* ... */
#else
assert(tud_inited());
#endif
if (!mutex_is_initialized(&stdio_usb_mutex)) mutex_init(&stdio_usb_mutex);
bool rc = true;
#if !LIB_TINYUSB_DEVICE
#ifdef PICO_STDIO_USB_LOW_PRIORITY_IRQ
user_irq_claim(PICO_STDIO_USB_LOW_PRIORITY_IRQ);
#else
low_priority_irq_num = (uint8_t) user_irq_claim_unused(true);
#endif
irq_set_exclusive_handler(low_priority_irq_num, low_priority_worker_irq);
irq_set_enabled(low_priority_irq_num, true);
if (irq_has_shared_handler(USBCTRL_IRQ)) {
critical_section_init_with_lock_num(&one_shot_timer_crit_sec, spin_lock_claim_unused(true));
irq_add_shared_handler(USBCTRL_IRQ, usb_irq, PICO_SHARED_IRQ_HANDLER_LOWEST_ORDER_PRIORITY);
}if (irq_has_shared_handler(USBCTRL_IRQ)) { ... } else {
memset(&one_shot_timer_crit_sec, 0, sizeof(one_shot_timer_crit_sec));
rc = add_alarm_in_us(PICO_STDIO_USB_TASK_INTERVAL_US, timer_task, NULL, true) >= 0;
}else { ... }
/* ... */#endif
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;
}if (stdio_usb_connected()) { ... }
sleep_ms(10);
...} while (!time_reached(until));/* ... */
#endif
}if (rc) { ... }
return rc;
}{ ... }
bool stdio_usb_deinit(void) {
if (get_core_num() != alarm_pool_core_num(alarm_pool_get_default())) {
assert(false);
return false;
}if (get_core_num() != alarm_pool_core_num(alarm_pool_get_default())) { ... }
assert(tud_inited());
bool rc = true;
stdio_set_driver_enabled(&stdio_usb, false);
#if PICO_STDIO_USB_DEINIT_DELAY_MS != 0
sleep_ms(PICO_STDIO_USB_DEINIT_DELAY_MS);
#endif
#if !LIB_TINYUSB_DEVICE
if (irq_has_shared_handler(USBCTRL_IRQ)) {
spin_lock_unclaim(spin_lock_get_num(one_shot_timer_crit_sec.spin_lock));
critical_section_deinit(&one_shot_timer_crit_sec);
irq_remove_handler(USBCTRL_IRQ, usb_irq);
}if (irq_has_shared_handler(USBCTRL_IRQ)) { ... } else {
}else { ... }
irq_set_enabled(low_priority_irq_num, false);
user_irq_unclaim(low_priority_irq_num);/* ... */
#endif
return rc;
}{ ... }
bool stdio_usb_connected(void) {
#if PICO_STDIO_USB_CONNECTION_WITHOUT_DTR
return tud_ready();
#else
return tud_cdc_connected();/* ... */
#endif
}{ ... }
/* ... */#else
#warning stdio USB was configured along with user use of TinyUSB device mode, but CDC is not enabled
bool stdio_usb_init(void) {
return false;
}stdio_usb_init (void) { ... }
/* ... */#endif /* ... */
#else
#warning stdio USB was configured, but is being disabled as TinyUSB host is explicitly linked
bool stdio_usb_init(void) {
return false;
}stdio_usb_init (void) { ... }
/* ... */#endif