Skip to content

Commit 7858601

Browse files
authored
stdio_usb improvements (#871)
Use shared IRQ if available to avoid 1ms timer. Allow use of stdio_usb with user's tinyusb setup if it has CDC
1 parent 0bdd463 commit 7858601

File tree

5 files changed

+72
-10
lines changed

5 files changed

+72
-10
lines changed

src/rp2_common/hardware_irq/include/hardware/irq.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,14 @@ void irq_add_shared_handler(uint num, irq_handler_t handler, uint8_t order_prior
252252
*/
253253
void irq_remove_handler(uint num, irq_handler_t handler);
254254

255+
/*! \brief Determine if the current handler for the given number is shared
256+
* \ingroup hardware_irq
257+
*
258+
* \param num Interrupt number \ref interrupt_nums
259+
* \param return true if the specified IRQ has a shared handler
260+
*/
261+
bool irq_has_shared_handler(uint num);
262+
255263
/*! \brief Get the current IRQ handler for the specified IRQ from the currently installed hardware vector table (VTOR)
256264
* of the execution core
257265
* \ingroup hardware_irq

src/rp2_common/hardware_irq/irq.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,21 @@ static int8_t irq_hander_chain_free_slot_head;
9595
static inline bool is_shared_irq_raw_handler(irq_handler_t raw_handler) {
9696
return (uintptr_t)raw_handler - (uintptr_t)irq_handler_chain_slots < sizeof(irq_handler_chain_slots);
9797
}
98+
99+
bool irq_has_shared_handler(uint irq_num) {
100+
check_irq_param(irq_num);
101+
irq_handler_t handler = irq_get_vtable_handler(irq_num);
102+
return handler && is_shared_irq_raw_handler(handler);
103+
}
104+
98105
#else
99106
#define is_shared_irq_raw_handler(h) false
107+
bool irq_has_shared_handler(uint irq_num) {
108+
return false;
109+
}
100110
#endif
101111

112+
102113
irq_handler_t irq_get_vtable_handler(uint num) {
103114
check_irq_param(num);
104115
return get_vtable()[16 + num];

src/rp2_common/pico_stdio_usb/include/tusb_config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
#include "pico/stdio_usb.h"
3131

32+
#if !defined(LIB_TINYUSB_HOST) && !defined(LIB_TINYUSB_DEVICE)
3233
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE)
3334

3435
#define CFG_TUD_CDC (1)
@@ -38,3 +39,5 @@
3839
// We use a vendor specific interface but with our own driver
3940
#define CFG_TUD_VENDOR (0)
4041
#endif
42+
43+
#endif

src/rp2_common/pico_stdio_usb/reset_interface.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include "pico/bootrom.h"
99

10+
#if !defined(LIB_TINYUSB_HOST) && !defined(LIB_TINYUSB_DEVICE)
11+
1012
#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && !(PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL || PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT)
1113
#warning PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE has been selected but neither PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL nor PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT have been selected.
1214
#endif
@@ -110,3 +112,4 @@ void tud_cdc_line_coding_cb(__unused uint8_t itf, cdc_line_coding_t const* p_lin
110112
}
111113
#endif
112114

115+
#endif

src/rp2_common/pico_stdio_usb/stdio_usb.c

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,34 @@
44
* SPDX-License-Identifier: BSD-3-Clause
55
*/
66

7-
#if !defined(LIB_TINYUSB_HOST) && !defined(LIB_TINYUSB_DEVICE)
7+
#ifndef LIB_TINYUSB_HOST
88
#include "tusb.h"
9+
#include "pico/stdio_usb.h"
10+
11+
// these may not be set if the user is providing tud support (i.e. LIB_TINYUSB_DEVICE is 1 because
12+
// the user linked in tinyusb_device) but they haven't selected CDC
13+
#if (CFG_TUD_ENABLED | TUSB_OPT_DEVICE_ENABLED) && CFG_TUD_CDC
914

15+
#include "pico/binary_info.h"
1016
#include "pico/time.h"
1117
#include "pico/stdio/driver.h"
12-
#include "pico/binary_info.h"
1318
#include "pico/mutex.h"
1419
#include "hardware/irq.h"
1520

21+
static mutex_t stdio_usb_mutex;
22+
#ifndef NDEBUG
23+
static uint8_t stdio_usb_core_num;
24+
#endif
25+
26+
// when tinyusb_device is explicitly linked we do no background tud processing
27+
#if !LIB_TINYUSB_DEVICE
1628
#ifdef PICO_STDIO_USB_LOW_PRIORITY_IRQ
1729
static_assert(PICO_STDIO_USB_LOW_PRIORITY_IRQ >= NUM_IRQS - NUM_USER_IRQS, "");
1830
#define low_priority_irq_num PICO_STDIO_USB_LOW_PRIORITY_IRQ
1931
#else
2032
static uint8_t low_priority_irq_num;
2133
#endif
2234

23-
static mutex_t stdio_usb_mutex;
24-
2535
static void low_priority_worker_irq(void) {
2636
// if the mutex is already owned, then we are in user code
2737
// in this file which will do a tud_task itself, so we'll just do nothing
@@ -32,10 +42,16 @@ static void low_priority_worker_irq(void) {
3242
}
3343
}
3444

45+
static void usb_irq(void) {
46+
irq_set_pending(low_priority_irq_num);
47+
}
48+
3549
static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) {
50+
assert(stdio_usb_core_num == get_core_num()); // if this fails, you have initialized stdio_usb on the wrong core
3651
irq_set_pending(low_priority_irq_num);
3752
return PICO_STDIO_USB_TASK_INTERVAL_US;
3853
}
54+
#endif
3955

4056
static void stdio_usb_out_chars(const char *buf, int length) {
4157
static uint64_t last_avail_time;
@@ -95,13 +111,23 @@ stdio_driver_t stdio_usb = {
95111
};
96112

97113
bool stdio_usb_init(void) {
114+
#ifndef NDEBUG
115+
stdio_usb_core_num = (uint8_t)get_core_num();
116+
#endif
98117
#if !PICO_NO_BI_STDIO_USB
99118
bi_decl_if_func_used(bi_program_feature("USB stdin / stdout"));
100119
#endif
101120

102-
// initialize TinyUSB
121+
#if !defined(LIB_TINYUSB_DEVICE)
122+
// initialize TinyUSB, as user hasn't explicitly linked it
103123
tusb_init();
124+
#else
125+
assert(tud_inited()); // we expect the caller to have initialized if they are using TinyUSB
126+
#endif
104127

128+
mutex_init(&stdio_usb_mutex);
129+
bool rc = true;
130+
#if !LIB_TINYUSB_DEVICE
105131
#ifdef PICO_STDIO_USB_LOW_PRIORITY_IRQ
106132
user_irq_claim(PICO_STDIO_USB_LOW_PRIORITY_IRQ);
107133
#else
@@ -110,8 +136,13 @@ bool stdio_usb_init(void) {
110136
irq_set_exclusive_handler(low_priority_irq_num, low_priority_worker_irq);
111137
irq_set_enabled(low_priority_irq_num, true);
112138

113-
mutex_init(&stdio_usb_mutex);
114-
bool rc = add_alarm_in_us(PICO_STDIO_USB_TASK_INTERVAL_US, timer_task, NULL, true);
139+
if (irq_has_shared_handler(USBCTRL_IRQ)) {
140+
// we can use a shared handler to notice when there may be work to do
141+
irq_add_shared_handler(USBCTRL_IRQ, usb_irq, PICO_SHARED_IRQ_HANDLER_LOWEST_ORDER_PRIORITY);
142+
} else {
143+
rc = add_alarm_in_us(PICO_STDIO_USB_TASK_INTERVAL_US, timer_task, NULL, true);
144+
}
145+
#endif
115146
if (rc) {
116147
stdio_set_driver_enabled(&stdio_usb, true);
117148
#if PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS
@@ -138,9 +169,15 @@ bool stdio_usb_connected(void) {
138169
return tud_cdc_connected();
139170
}
140171
#else
141-
#include "pico/stdio_usb.h"
142-
#warning stdio USB was configured, but is being disabled as TinyUSB is explicitly linked
172+
#warning stdio USB was configured along with user use of TinyUSB device mode, but CDC is not enabled
143173
bool stdio_usb_init(void) {
144174
return false;
145175
}
146-
#endif
176+
#endif // CFG_TUD_ENABLED && CFG_TUD_CDC
177+
#else
178+
#warning stdio USB was configured, but is being disabled as TinyUSB host is explicitly linked
179+
bool stdio_usb_init(void) {
180+
return false;
181+
}
182+
#endif // !LIB_TINYUSB_HOST
183+

0 commit comments

Comments
 (0)