Skip to content

Commit c5128a9

Browse files
Add CLI option to enable detection of devices with reset interface
1 parent b203309 commit c5128a9

File tree

4 files changed

+59
-20
lines changed

4 files changed

+59
-20
lines changed

README.md

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,14 @@ PICOTOOL:
7070
Tool for interacting with a RP2040 device in BOOTSEL mode, or with a RP2040 binary
7171
7272
SYNOPSIS:
73-
picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [-f] [-F]
73+
picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [-i] [-f] [-F]
7474
picotool info [-b] [-p] [-d] [-l] [-a] <filename> [-t <type>]
75-
picotool load [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [-f] [-F]
76-
picotool save [-p] [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
77-
picotool save -a [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
78-
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
79-
picotool verify [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>] [-r <from> <to>] [-o <offset>]
80-
picotool reboot [-a] [-u] [--bus <bus>] [--address <addr>] [-f] [-F]
75+
picotool load [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [-i] [-f] [-F]
76+
picotool save [-p] [--bus <bus>] [--address <addr>] [-i] [-f] [-F] <filename> [-t <type>]
77+
picotool save -a [--bus <bus>] [--address <addr>] [-i] [-f] [-F] <filename> [-t <type>]
78+
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [-i] [-f] [-F] <filename> [-t <type>]
79+
picotool verify [--bus <bus>] [--address <addr>] [-i] [-f] [-F] <filename> [-t <type>] [-r <from> <to>] [-o <offset>]
80+
picotool reboot [-a] [-u] [--bus <bus>] [--address <addr>] [-i] [-f] [-F]
8181
picotool version [-s]
8282
picotool help [<cmd>]
8383
@@ -111,7 +111,7 @@ INFO:
111111
Without any arguments, this will display basic information for all connected RP2040 devices in BOOTSEL mode
112112
113113
SYNOPSIS:
114-
picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [-f] [-F]
114+
picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [-i] [-f] [-F]
115115
picotool info [-b] [-p] [-d] [-l] [-a] <filename> [-t <type>]
116116
117117
OPTIONS:
@@ -133,6 +133,9 @@ TARGET SELECTION:
133133
Filter devices by USB bus number
134134
--address <addr>
135135
Filter devices by USB device address
136+
-i, --detect-reset-interface
137+
Enable detection of devices with a custom USB vendor interface compatible with stdio_usb reset interface.
138+
The stdio_usb reset interface is implemented by Pico SDK in default USB serial setup
136139
-f, --force
137140
Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing
138141
the command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
@@ -218,7 +221,7 @@ LOAD:
218221
Load the program / memory range stored in a file onto the device.
219222
220223
SYNOPSIS:
221-
picotool load [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [-f] [-F]
224+
picotool load [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [-i] [-f] [-F]
222225
223226
OPTIONS:
224227
Post load actions
@@ -249,6 +252,9 @@ OPTIONS:
249252
Filter devices by USB bus number
250253
--address <addr>
251254
Filter devices by USB device address
255+
-i, --detect-reset-interface
256+
Enable detection of devices with a custom USB vendor interface compatible with stdio_usb reset interface.
257+
The stdio_usb reset interface is implemented by Pico SDK in default USB serial setup
252258
-f, --force
253259
Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing
254260
the command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
@@ -275,9 +281,9 @@ SAVE:
275281
Save the program / memory stored in flash on the device to a file.
276282
277283
SYNOPSIS:
278-
picotool save [-p] [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
279-
picotool save -a [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
280-
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
284+
picotool save [-p] [--bus <bus>] [--address <addr>] [-i] [-f] [-F] <filename> [-t <type>]
285+
picotool save -a [--bus <bus>] [--address <addr>] [-i] [-f] [-F] <filename> [-t <type>]
286+
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [-i] [-f] [-F] <filename> [-t <type>]
281287
282288
OPTIONS:
283289
Selection of data to save
@@ -297,6 +303,9 @@ OPTIONS:
297303
Filter devices by USB bus number
298304
--address <addr>
299305
Filter devices by USB device address
306+
-i, --detect-reset-interface
307+
Enable detection of devices with a custom USB vendor interface compatible with stdio_usb reset interface.
308+
The stdio_usb reset interface is implemented by Pico SDK in default USB serial setup
300309
-f, --force
301310
Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing
302311
the command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
@@ -540,3 +549,17 @@ in binary.
540549
If you ctrl+c out of the middle of a long operation, then libusb seems to get a bit confused, which means we aren't able
541550
to unlock our lockout of USB MSD writes (we have turned them off so the user doesn't step on their own toes). Simply running
542551
`picotool info` again will unlock it properly the next time (or you can reboot the device).
552+
553+
### Support for custom stdio_usb compatible reset interface
554+
555+
When you include [`pico_stdio_usb`](https://www.raspberrypi.com/documentation/pico-sdk/runtime.html#pico_stdio_usb)
556+
module and enable default Pico SDK USB stack using
557+
[`stdio_usb_init()`](https://www.raspberrypi.com/documentation/pico-sdk/runtime.html#gab87bcfa3f24e5a3fe92a944f9eecc460),
558+
you also automatically include a custom [USB vendor interface](https://www.usb.org/defined-class-codes).
559+
This interface implements commands to restart the device either to bootloader or to the application.
560+
561+
However `picotool` does not recognize the device as standard RP2040 device,
562+
when it uses custom USB setup via [TinyUSB](https://docs.tinyusb.org/).
563+
Custom devices implementing stdio_usb compatible
564+
[reset interface](https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_stdio_usb/reset_interface.c)
565+
are supported using picotool `--detect-reset-interface` or `-i` CLI option.

main.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ struct _settings {
264264
bool reboot_app_specified = false;
265265
bool force = false;
266266
bool force_no_reboot = false;
267+
bool detect_reset_interface = false;
267268

268269
struct {
269270
bool show_basic = false;
@@ -297,7 +298,10 @@ auto device_selection =
297298
(option("--bus") & integer("bus").min_value(0).max_value(255).set(settings.bus)
298299
.if_missing([] { return "missing bus number"; })) % "Filter devices by USB bus number" +
299300
(option("--address") & integer("addr").min_value(1).max_value(127).set(settings.address)
300-
.if_missing([] { return "missing address"; })) % "Filter devices by USB device address"
301+
.if_missing([] { return "missing address"; })) % "Filter devices by USB device address" +
302+
option('i', "--detect-reset-interface").set(settings.detect_reset_interface)
303+
% "Enable detection of devices with a custom USB vendor interface compatible with stdio_usb reset interface."
304+
"The stdio_usb reset interface is implemented by Pico SDK in default USB serial setup."
301305
#if !defined(_WIN32)
302306
+ option('f', "--force").set(settings.force) % "Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the command (unless the command itself is a 'reboot') the device will be rebooted back to application mode" +
303307
option('F', "--force-no-reboot").set(settings.force_no_reboot) % "Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the command (unless the command itself is a 'reboot') the device will be left connected and accessible to picotool, but without the RPI-RP2 drive mounted"
@@ -2110,7 +2114,10 @@ static int reboot_device(libusb_device *device, bool bootsel, uint disable_mask=
21102114

21112115
bool reboot_command::execute(device_map &devices) {
21122116
if (settings.force) {
2113-
reboot_device(devices[dr_vidpid_stdio_usb][0].first, settings.reboot_usb);
2117+
auto &to_reboot = devices[dr_vidpid_stdio_usb].empty()
2118+
? devices[dr_vidpid_stdio_usb][0].first
2119+
: devices[dr_reset_interface][0].first;
2120+
reboot_device(to_reboot, settings.reboot_usb);
21142121
if (!quiet) {
21152122
if (settings.reboot_usb) {
21162123
std::cout << "The device was asked to reboot into BOOTSEL mode.\n";
@@ -2230,7 +2237,7 @@ int main(int argc, char **argv) {
22302237
if (handle) {
22312238
to_close.push_back(handle);
22322239
}
2233-
if (result != dr_error) {
2240+
if (result != dr_error && (result != dr_reset_interface || settings.detect_reset_interface)) {
22342241
devices[result].push_back(std::make_pair(*dev, handle));
22352242
}
22362243
}
@@ -2242,7 +2249,7 @@ int main(int argc, char **argv) {
22422249
// fall thru
22432250
case cmd::device_support::one:
22442251
if (devices[dr_vidpid_bootrom_ok].empty() &&
2245-
(!settings.force || devices[dr_vidpid_stdio_usb].empty())) {
2252+
(!settings.force || (devices[dr_vidpid_stdio_usb].empty() && devices[dr_reset_interface].empty()))) {
22462253
bool had_note = false;
22472254
fos << missing_device_string(tries>0);
22482255
if (tries > 0) {
@@ -2276,19 +2283,25 @@ int main(int argc, char **argv) {
22762283
#if defined(_WIN32)
22772284
printer(dr_vidpid_stdio_usb,
22782285
" appears to be a RP2040 device with a USB serial connection, not in BOOTSEL mode. You can force reboot into BOOTSEL mode via 'picotool reboot -f -u' first.");
2286+
printer(dr_reset_interface,
2287+
" appears to be a RP2040 device with a USB reset interface, not in BOOTSEL mode. You can force reboot into BOOTSEL mode via 'picotool reboot -f -u -i' first.");
22792288
#else
22802289
printer(dr_vidpid_stdio_usb,
22812290
" appears to be a RP2040 device with a USB serial connection, so consider -f (or -F) to force reboot in order to run the command.");
2291+
printer(dr_reset_interface,
2292+
" appears to be a RP2040 device with a USB reset interface, so consider -f (or -F) to force reboot in order to run the command.");
22822293
#endif
22832294
} else {
22842295
// special case message for what is actually just reboot (the only command that doesn't require reboot first)
22852296
printer(dr_vidpid_stdio_usb,
22862297
" appears to be a RP2040 device with a USB serial connection, so consider -f to force the reboot.");
2298+
printer(dr_reset_interface,
2299+
" appears to be a RP2040 device with a USB reset interface, so consider -f to force the reboot.");
22872300
}
22882301
rc = ERROR_NO_DEVICE;
22892302
} else if (supported == cmd::device_support::one) {
22902303
if (devices[dr_vidpid_bootrom_ok].size() > 1 ||
2291-
(devices[dr_vidpid_bootrom_ok].empty() && devices[dr_vidpid_stdio_usb].size() > 1)) {
2304+
(devices[dr_vidpid_bootrom_ok].empty() && (devices[dr_vidpid_stdio_usb].size() + devices[dr_reset_interface].size()) > 1)) {
22922305
fail(ERROR_NOT_POSSIBLE, "Command requires a single RP2040 device to be targeted.");
22932306
}
22942307
if (!devices[dr_vidpid_bootrom_ok].empty()) {
@@ -2306,13 +2319,15 @@ int main(int argc, char **argv) {
23062319
}
23072320
if (!rc) {
23082321
if (settings.force && ctx) { // actually ctx should never be null as we are targeting device if force is set, but still
2309-
if (devices[dr_vidpid_stdio_usb].size() != 1) {
2322+
if ((devices[dr_vidpid_stdio_usb].size() + devices[dr_reset_interface].size()) != 1) {
23102323
fail(ERROR_NOT_POSSIBLE,
23112324
"Forced command requires a single rebootable RP2040 device to be targeted.");
23122325
}
23132326
if (selected_cmd->force_requires_pre_reboot()) {
23142327
// we reboot into BOOTSEL mode and disable MSC interface (the 1 here)
2315-
auto &to_reboot = devices[dr_vidpid_stdio_usb][0].first;
2328+
auto &to_reboot = devices[dr_vidpid_stdio_usb].empty()
2329+
? devices[dr_vidpid_stdio_usb][0].first
2330+
: devices[dr_reset_interface][0].first;
23162331
reboot_device(to_reboot, true, 1);
23172332
fos << "The device was asked to reboot into BOOTSEL mode so the command can be executed.\n\n";
23182333
for (const auto &handle : to_close) {

picoboot_connection/picoboot_connection.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d
7676
if (0xff == config->interface[i].altsetting[0].bInterfaceClass &&
7777
RESET_INTERFACE_SUBCLASS == config->interface[i].altsetting[0].bInterfaceSubClass &&
7878
RESET_INTERFACE_PROTOCOL == config->interface[i].altsetting[0].bInterfaceProtocol) {
79-
return dr_vidpid_stdio_usb;
79+
return dr_reset_interface;
8080
}
8181
}
8282

picoboot_connection/picoboot_connection.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum picoboot_device_result {
2626
dr_vidpid_unknown,
2727
dr_error,
2828
dr_vidpid_stdio_usb,
29+
dr_reset_interface,
2930
};
3031

3132
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle);

0 commit comments

Comments
 (0)