Skip to content

Commit 329ec28

Browse files
committed
Add support for resetting RP2040 via the USB connection when using pico_stdio_usb
- setting baud rate to magic value (default=1200) will cause a reset to BOOTSEL mode - a VENDOR interface along side the CDC interface can be used to reset via refular flash boot, or into BOOTSEL mode with control for the reset_usb_boot parameters for the latter either method can be configured/enabled/disabled via #define
1 parent fb5a847 commit 329ec28

File tree

8 files changed

+186
-5
lines changed

8 files changed

+186
-5
lines changed

src/common/pico_binary_info/include/pico/binary_info.h

+2
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,7 @@
2323
#if !PICO_ON_DEVICE && !defined(PICO_NO_BINARY_INFO)
2424
#define PICO_NO_BINARY_INFO 1
2525
#endif
26+
#if !PICO_NO_BINARY_INFO
2627
#include "pico/binary_info/code.h"
28+
#endif
2729
#endif

src/rp2_common/pico_stdio_usb/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ if (TARGET tinyusb_device_unmarked)
44
target_include_directories(pico_stdio_usb INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
55

66
target_sources(pico_stdio_usb INTERFACE
7+
${CMAKE_CURRENT_LIST_DIR}/reset_interface.c
78
${CMAKE_CURRENT_LIST_DIR}/stdio_usb.c
89
${CMAKE_CURRENT_LIST_DIR}/stdio_usb_descriptors.c
910
)

src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h

+33-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
* Note this library is a developer convenience. It is not applicable in all cases; for one it takes full control of the USB device precluding your
2020
* use of the USB in device or host mode. For this reason, this library will automatically disengage if you try to using it alongside \ref tinyusb_device or
2121
* \ref tinyusb_host. It also takes control of a lower level IRQ and sets up a periodic background task.
22+
*
23+
* This library also includes (by default) functionality to enable the RP2040 to be reset over the USB interface.
2224
*/
2325

2426
// PICO_CONFIG: PICO_STDIO_USB_DEFAULT_CRLF, Default state of CR/LF translation for USB output, type=bool, default=PICO_STDIO_DEFAULT_CRLF, group=pico_stdio_usb
@@ -31,7 +33,7 @@
3133
#define PICO_STDIO_USB_STDOUT_TIMEOUT_US 500000
3234
#endif
3335

34-
// todo perhaps unnecessarily high?
36+
// todo perhaps unnecessarily frequent?
3537
// PICO_CONFIG: PICO_STDIO_USB_TASK_INTERVAL_US, Period of microseconds between calling tud_task in the background, default=1000, advanced=true, group=pico_stdio_usb
3638
#ifndef PICO_STDIO_USB_TASK_INTERVAL_US
3739
#define PICO_STDIO_USB_TASK_INTERVAL_US 1000
@@ -42,6 +44,36 @@
4244
#define PICO_STDIO_USB_LOW_PRIORITY_IRQ 31
4345
#endif
4446

47+
// PICO_CONFIG: PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE, Enable/disable resetting into BOOTSEL mode if the host sets the baud rate to a magic value (PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE), type=bool, default=1, group=pico_stdio_usb
48+
#ifndef PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE
49+
#define PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE 1
50+
#endif
51+
52+
// PICO_CONFIG: PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE, baud rate that if selected causes a reset into BOOTSEL mode (if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE==1), default=1200, group=pico_stdio_usb
53+
#ifndef PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE
54+
#define PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE 1200
55+
#endif
56+
57+
// PICO_CONFIG: PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE, Enable/disable resetting into BOOTSEL mode via an additional VENDOR USB interface - enables picotool based reset, type=bool, default=1, group=pico_stdio_usb
58+
#ifndef PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
59+
#define PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE 1
60+
#endif
61+
62+
// PICO_CONFIG: PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL, If vendor reset interface is included allow rebooting to BOOTSEL mode, type=bool, default=1, group=pico_stdio_usb
63+
#ifndef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL
64+
#define PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL 1
65+
#endif
66+
67+
// PICO_CONFIG: PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT, If vendor reset interface is included allow rebooting with regular flash boot, type=bool, default=1, group=pico_stdio_usb
68+
#ifndef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT
69+
#define PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT 1
70+
#endif
71+
72+
// PICO_CONFIG: PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS, delays in ms before rebooting via regular flash boot, default=100, group=pico_stdio_usb
73+
#ifndef PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS
74+
#define PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS 100
75+
#endif
76+
4577
#ifdef __cplusplus
4678
extern "C" {
4779
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#ifndef _PICO_STDIO_USB_RESET_INTERFACE_H
8+
#define _PICO_STDIO_USB_RESET_INTERFACE_H
9+
10+
// We use VENDOR, 0, 0 for PICOBOOT, so lets use VENDOR, 0, 1 for RESET
11+
12+
// VENDOR sub-class for the reset interface
13+
#define RESET_INTERFACE_SUBCLASS 0x00
14+
// VENDOR protocol for the reset interface
15+
#define RESET_INTERFACE_PROTOCOL 0x01
16+
17+
// CONTROL requests:
18+
19+
// reset to BOOTSEL
20+
#define RESET_REQUEST_BOOTSEL 0x01
21+
// regular flash boot
22+
#define RESET_REQUEST_FLASH 0x02
23+
#endif

src/rp2_common/pico_stdio_usb/include/tusb_config.h

+4
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,14 @@
2727
#ifndef _PICO_STDIO_USB_TUSB_CONFIG_H
2828
#define _PICO_STDIO_USB_TUSB_CONFIG_H
2929

30+
#include "pico/stdio_usb.h"
31+
3032
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE)
3133

3234
#define CFG_TUD_CDC (1)
3335
#define CFG_TUD_CDC_RX_BUFSIZE (256)
3436
#define CFG_TUD_CDC_TX_BUFSIZE (256)
3537

38+
// We use a vendor specific interface but with our own driver
39+
#define CFG_TUD_VENDOR (0)
3640
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
#include "pico/stdio_usb.h"
7+
8+
#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
9+
#include "pico/bootrom.h"
10+
#include "pico/stdio_usb/reset_interface.h"
11+
#include "hardware/watchdog.h"
12+
#include "device/usbd_pvt.h"
13+
14+
static uint8_t itf_num;
15+
16+
static void resetd_init(void) {
17+
}
18+
19+
static void resetd_reset(uint8_t __unused rhport) {
20+
itf_num = 0;
21+
}
22+
23+
static uint16_t resetd_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
24+
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
25+
RESET_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
26+
RESET_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
27+
28+
uint16_t const drv_len = sizeof(tusb_desc_interface_t);
29+
TU_VERIFY(max_len >= drv_len, 0);
30+
31+
itf_num = itf_desc->bInterfaceNumber;
32+
return drv_len;
33+
}
34+
35+
// Support for parameterized reset via vendor interface control request
36+
static bool resetd_control_request_cb(uint8_t __unused rhport, tusb_control_request_t const *request) {
37+
if (request->wIndex == itf_num) {
38+
#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL
39+
if (request->bRequest == RESET_REQUEST_BOOTSEL) {
40+
uint gpio_mask = 0;
41+
if (request->wValue & 0x100) {
42+
gpio_mask = 1u << (request->wValue >> 9u);
43+
}
44+
reset_usb_boot(gpio_mask, request->wValue & 0x7f);
45+
// does not return, otherwise we'd return true
46+
}
47+
#endif
48+
#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT
49+
if (request->bRequest == RESET_REQUEST_FLASH) {
50+
watchdog_reboot(0, SRAM_END, PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS);
51+
return true;
52+
}
53+
#endif
54+
}
55+
return false;
56+
}
57+
58+
static bool resetd_control_complete_cb(uint8_t __unused rhport, tusb_control_request_t __unused const *request) {
59+
return true;
60+
}
61+
62+
static bool resetd_xfer_cb(uint8_t __unused rhport, uint8_t __unused ep_addr, xfer_result_t __unused result, uint32_t __unused xferred_bytes) {
63+
return true;
64+
}
65+
66+
static usbd_class_driver_t const _resetd_driver =
67+
{
68+
#if CFG_TUSB_DEBUG >= 2
69+
.name = "RESET",
70+
#endif
71+
.init = resetd_init,
72+
.reset = resetd_reset,
73+
.open = resetd_open,
74+
.control_request = resetd_control_request_cb,
75+
.control_complete = resetd_control_complete_cb,
76+
.xfer_cb = resetd_xfer_cb,
77+
.sof = NULL
78+
};
79+
80+
// Implement callback to add our custom driver
81+
usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
82+
*driver_count = 1;
83+
return &_resetd_driver;
84+
}
85+
#endif

src/rp2_common/pico_stdio_usb/stdio_usb.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
#include "tusb.h"
99

1010
#include "pico/time.h"
11-
#include "pico/stdio_usb.h"
1211
#include "pico/stdio/driver.h"
12+
#include "pico/bootrom.h"
13+
1314
#include "pico/binary_info.h"
1415
#include "hardware/irq.h"
15-
1616
static_assert(PICO_STDIO_USB_LOW_PRIORITY_IRQ > RTC_IRQ, ""); // note RTC_IRQ is currently the last one
1717
static mutex_t stdio_usb_mutex;
1818

@@ -106,10 +106,21 @@ bool stdio_usb_init(void) {
106106
}
107107
return rc;
108108
}
109+
110+
#if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE
111+
// Support for default BOOTSEL reset by changing baud rate
112+
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding) {
113+
if (p_line_coding->bit_rate == PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE) {
114+
reset_usb_boot(0, 0);
115+
}
116+
}
117+
#endif
118+
109119
#else
110120
#include "pico/stdio_usb.h"
111121
#warning stdio USB was configured, but is being disabled as TinyUSB is explicitly linked
112122
bool stdio_usb_init(void) {
113123
return false;
114124
}
115125
#endif
126+

src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c

+25-2
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,26 @@
2929
#if !defined(TINYUSB_HOST_LINKED) && !defined(TINYUSB_DEVICE_LINKED)
3030

3131
#include "tusb.h"
32+
#include "pico/stdio_usb/reset_interface.h"
3233

3334
#define USBD_VID (0x2E8A) // Raspberry Pi
3435
#define USBD_PID (0x000a) // Raspberry Pi Pico SDK CDC
3536

37+
#define TUD_RPI_RESET_DESC_LEN 9
38+
#if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
3639
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
40+
#else
41+
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_RPI_RESET_DESC_LEN)
42+
#endif
3743
#define USBD_MAX_POWER_MA (250)
3844

39-
#define USBD_ITF_CDC (0) // needs 2 interfaces
40-
#define USBD_ITF_MAX (2)
45+
#define USBD_ITF_CDC (0) // needs 2 interfaces
46+
#if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
47+
#define USBD_ITF_MAX (2)
48+
#else
49+
#define USBD_ITF_RPI_RESET (2)
50+
#define USBD_ITF_MAX (3)
51+
#endif
4152

4253
#define USBD_CDC_EP_CMD (0x81)
4354
#define USBD_CDC_EP_OUT (0x02)
@@ -50,6 +61,7 @@
5061
#define USBD_STR_PRODUCT (0x02)
5162
#define USBD_STR_SERIAL (0x03)
5263
#define USBD_STR_CDC (0x04)
64+
#define USBD_STR_RPI_RESET (0x05)
5365

5466
// Note: descriptors returned from callbacks must exist long enough for transfer to complete
5567

@@ -70,19 +82,30 @@ static const tusb_desc_device_t usbd_desc_device = {
7082
.bNumConfigurations = 1,
7183
};
7284

85+
#define TUD_RPI_RESET_DESCRIPTOR(_itfnum, _stridx) \
86+
/* Interface */\
87+
9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_VENDOR_SPECIFIC, RESET_INTERFACE_SUBCLASS, RESET_INTERFACE_PROTOCOL, _stridx,
88+
7389
static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = {
7490
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN,
7591
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA),
7692

7793
TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD,
7894
USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE),
95+
96+
#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
97+
TUD_RPI_RESET_DESCRIPTOR(USBD_ITF_RPI_RESET, USBD_STR_RPI_RESET)
98+
#endif
7999
};
80100

81101
static const char *const usbd_desc_str[] = {
82102
[USBD_STR_MANUF] = "Raspberry Pi",
83103
[USBD_STR_PRODUCT] = "Pico",
84104
[USBD_STR_SERIAL] = "000000000000", // TODO
85105
[USBD_STR_CDC] = "Board CDC",
106+
#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
107+
[USBD_STR_RPI_RESET] = "Reset",
108+
#endif
86109
};
87110

88111
const uint8_t *tud_descriptor_device_cb(void) {

0 commit comments

Comments
 (0)