Select one of the symbols to view example projects that use it.
 
Outline
#include "tusb.h"
#include "pico/stdio_usb.h"
#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"
stdio_usb_mutex
chars_available_callback
chars_available_param
one_shot_timer_crit_sec
one_shot_timer_pending
#define low_priority_irq_num
low_priority_irq_num
timer_task(alarm_id_t, void *)
low_priority_worker_irq()
usb_irq()
stdio_usb_out_chars(const char *, int)
stdio_usb_out_flush()
stdio_usb_in_chars(char *, int)
stdio_usb_set_chars_available_callback(void (*)(void *), void *)
stdio_usb
stdio_usb_init()
stdio_usb_deinit()
stdio_usb_connected()
Files
loading...
SourceVuRaspberry Pi Pico SDK and ExamplesPicoSDKsrc/rp2_common/pico_stdio_usb/stdio_usb.c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause *//* ... */ #ifndef LIB_TINYUSB_HOST #include "tusb.h" #include "pico/stdio_usb.h" // these may not be set if the user is providing tud support (i.e. LIB_TINYUSB_DEVICE is 1 because // the user linked in tinyusb_device) but they haven't selected CDC #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" // for usbd_defer_func 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 // when tinyusb_device is explicitly linked we do no background tud processing #if !LIB_TINYUSB_DEVICE // if this crit_sec is initialized, we are not in periodic timer mode, and must make sure // we don't either create multiple one shot timers, or miss creating one. this crit_sec // is used to protect the one_shot_timer_pending flag 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; // don't repeat }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; // don't repeat }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 the mutex is already owned, then we are in non IRQ code in this file. // // it would seem simplest to just let that code call tud_task() at the end, however this // code might run during the call to tud_task() and we might miss a necessary tud_task() call // // if we are using a periodic timer (crit_sec is not initialized in this case), // then we are happy just to wait until the next tick, however when we are not using a periodic timer, // we must kick off a one-shot timer to make sure the tud_task() DOES run (this method // will be called again as a result, and will try the mutex_try_enter again, and if that fails // create another one shot timer again, and so on). 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 { // reset our timeout 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) { // note we perform this check outside the lock, to try and prevent possible deadlock conditions // with printf in IRQs (which we will escape through timeouts elsewhere, but that would be less graceful). // // these are just checks of state, so we can call them while not holding the lock. // they may be wrong, but only if we are in the middle of a tud_task call, in which case at worst // we will mistakenly think we have data available when we do not (we will check again), or // tud_task will complete running and we will check the right values the next time. // 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; // would deadlock otherwise }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 { // because our mutex use may starve out the background task, run tud_task here (we own the mutex) 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())) { // included an assertion here rather than just returning false, as this is likely // a coding bug, rather than anything else. 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) // initialize TinyUSB, as user hasn't explicitly linked it tusb_init();/* ... */ #else assert(tud_inited()); // we expect the caller to have initialized if they are using TinyUSB #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)); // we can use a shared handler to notice when there may be work to do irq_add_shared_handler(USBCTRL_IRQ, usb_irq, PICO_SHARED_IRQ_HANDLER_LOWEST_ORDER_PRIORITY); }if (irq_has_shared_handler(USBCTRL_IRQ)) { ... } else { // we use initialization state of the one_shot_timer_critsec as a flag 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())) { // included an assertion here rather than just returning false, as this is likely // a coding bug, rather than anything else. assert(false); return false; }if (get_core_num() != alarm_pool_core_num(alarm_pool_get_default())) { ... } assert(tud_inited()); // we expect the caller to have initialized when calling sdio_usb_init 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); // we can use a shared handler to notice when there may be work to do irq_remove_handler(USBCTRL_IRQ, usb_irq); }if (irq_has_shared_handler(USBCTRL_IRQ)) { ... } else { // timer is disabled by disabling the irq }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 // this actually checks DTR 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 // CFG_TUD_ENABLED && CFG_TUD_CDC/* ... */ #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 // !LIB_TINYUSB_HOST
Details
Show:
from
Types: Columns:
This file uses the notable symbols shown below. Click anywhere in the file to view more details.