From 9557ea801c93f3047ff4cd10650fb396a68a6a9e Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Wed, 22 Jul 2026 18:19:30 +0100 Subject: [PATCH 1/7] Add support for rebooting debugprobes Will work with the current master branch, and the next firmware release --- README.md | 126 ++++++++++++++++++++++ main.cpp | 98 +++++++++++++---- picoboot_connection/picoboot_connection.c | 73 ++++++++++--- picoboot_connection/picoboot_connection.h | 23 ++-- 4 files changed, 278 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index d3ec328f..ccf500f2 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,10 @@ TARGET SELECTION: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -139,6 +143,8 @@ TARGET SELECTION: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -247,6 +253,10 @@ TARGET SELECTION: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -256,6 +266,8 @@ TARGET SELECTION: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -362,6 +374,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -371,6 +387,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -440,6 +458,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -449,6 +471,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -525,6 +549,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -534,6 +562,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -586,6 +616,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -595,6 +629,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -679,6 +715,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -688,6 +728,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -874,6 +916,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -883,6 +929,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1149,6 +1197,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1158,6 +1210,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1265,6 +1319,10 @@ TARGET SELECTION: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1274,6 +1332,8 @@ TARGET SELECTION: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1338,6 +1398,10 @@ TARGET SELECTION: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1347,6 +1411,8 @@ TARGET SELECTION: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1398,6 +1464,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1407,6 +1477,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1453,6 +1525,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1462,6 +1538,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1550,6 +1628,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1559,6 +1641,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1626,6 +1710,10 @@ TARGET SELECTION: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1635,6 +1723,8 @@ TARGET SELECTION: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1842,6 +1932,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1851,6 +1945,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1913,6 +2009,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1922,6 +2022,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1988,6 +2090,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -1997,6 +2103,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -2059,6 +2167,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -2068,6 +2180,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -2130,6 +2244,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -2139,6 +2257,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -2197,6 +2317,10 @@ OPTIONS: --rp2040 Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems + --debugprobe + Force a DebugProbe device version >2.3.1 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 -f, --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 @@ -2206,6 +2330,8 @@ OPTIONS: 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 USB drive mounted + --only-force + Same as --force, but will only target devices not currently in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to diff --git a/main.cpp b/main.cpp index d53d5301..d60b998a 100644 --- a/main.cpp +++ b/main.cpp @@ -562,6 +562,7 @@ struct _settings { int led=DEFAULT_BOOTSEL_LED; bool active_low=false; bool force_rp2040 = false; + bool target_debugprobe = false; uint32_t offset = 0; uint32_t from = 0; uint32_t to = 0; @@ -573,6 +574,7 @@ struct _settings { int reboot_diagnostic_partition = BOOT_PARTITION_NONE; bool force = false; bool force_no_reboot = false; + bool only_force = false; string switch_cpu; uint32_t family_id = 0; model_t model = nullptr; @@ -708,12 +710,14 @@ auto device_selection = .if_missing([] { return "missing bus number"; })) % "Filter devices by USB bus number" + (option("--address") & integer("addr").min_value(1).max_value(127).set(settings.address) .if_missing([] { return "missing address"; })) % "Filter devices by USB device address" + - (option("--vid") & integer("vid").set(settings.vid).if_missing([] { return "missing vid"; })) % "Filter by vendor id" + + (option("--vid") & integer("vid").set(settings.vid).min(1).if_missing([] { return "missing vid"; })) % "Filter by vendor id" + (option("--pid") & integer("pid").set(settings.pid)) % "Filter by product id" + (option("--ser") & value("ser").set(settings.ser)) % "Filter by serial number" + option("--rp2040").set(settings.force_rp2040) % "Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems" + + option("--debugprobe").set(settings.target_debugprobe) % "Force a DebugProbe device version >2.3.1 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" + 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" + 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 USB drive mounted" + + option("--only-force").set(settings.only_force) % "Same as --force, but will only target devices not currently in BOOTSEL mode" + (option("--bootsel-led") & integer("gpio").set(settings.led)) % "Specify the GPIO for the BOOTSEL activity LED to flash (default " #if DEFAULT_BOOTSEL_LED < 0 @@ -4451,6 +4455,14 @@ string missing_device_string(bool wasRetry, bool requires_rp2350 = false) { } } else if (settings.bus != -1) { snprintf(buf, buf_len, "accessible %s devices in BOOTSEL mode were found on bus %d.", device_name, settings.bus); + } else if (settings.vid > 0) { + if (settings.pid != -1) { + snprintf(buf, buf_len, "accessible %s devices in BOOTSEL mode were found with vid %04x, pid %04x.", device_name, settings.vid, settings.pid); + } else { + snprintf(buf, buf_len, "accessible %s devices in BOOTSEL mode were found with vid %04x.", device_name, settings.vid); + } + } else if (settings.pid != -1) { + snprintf(buf, buf_len, "accessible %s devices in BOOTSEL mode were found with pid %04x.", device_name, settings.pid); } else if (!settings.ser.empty()) { snprintf(buf, buf_len, "accessible %s devices in BOOTSEL mode were found with serial number %s.", device_name, settings.ser.c_str()); } else { @@ -10054,8 +10066,8 @@ bool reboot_command::execute(device_map &devices) { if (!settings.switch_cpu.empty()) { fail(ERROR_ARGS, "--cpu may not be specified for forced reboot"); } - selected_chip = std::get<0>(devices[dr_vidpid_stdio_usb][0]); - reboot_device(std::get<1>(devices[dr_vidpid_stdio_usb][0]), std::get<2>(devices[dr_vidpid_stdio_usb][0]), settings.reboot_usb); + selected_chip = std::get<0>(devices[dr_vidpid_usb_reset][0]); + reboot_device(std::get<1>(devices[dr_vidpid_usb_reset][0]), std::get<2>(devices[dr_vidpid_usb_reset][0]), settings.reboot_usb); if (!quiet) { if (settings.reboot_usb) { std::cout << "The device was asked to reboot into BOOTSEL mode.\n"; @@ -10205,8 +10217,15 @@ int main(int argc, char **argv) { #if HAS_LIBUSB libusb_context *ctx = nullptr; + if (settings.target_debugprobe) { + if (settings.vid >= 0 || settings.pid >= 0) fail(ERROR_ARGS, "Cannot pass --debugprobe along with --vid or --pid"); + settings.vid = VENDOR_ID_RASPBERRY_PI; + settings.pid = PRODUCT_ID_DEBUGPROBE; + settings.only_force = true; + } + // save complicating the grammar - if (settings.force_no_reboot) settings.force = true; + if (settings.force_no_reboot || settings.only_force) settings.force = true; struct libusb_device **devs = nullptr; device_map devices; @@ -10255,13 +10274,22 @@ int main(int argc, char **argv) { } } auto supported = selected_cmd->get_device_support(); + bool bootsel_not_found = devices[dr_vidpid_bootrom_ok].empty(); + bool usb_reset_not_found = devices[dr_vidpid_usb_reset].empty(); + bool no_device_found = false; + if (settings.only_force) { + no_device_found = usb_reset_not_found; + } else if (settings.force) { + no_device_found = usb_reset_not_found && bootsel_not_found; + } else { + no_device_found = bootsel_not_found; + } switch (supported) { case cmd::device_support::zero_or_more: if (!settings.filenames[0].empty()) break; // fall thru case cmd::device_support::one: - if (devices[dr_vidpid_bootrom_ok].empty() && - (!settings.force || devices[dr_vidpid_stdio_usb].empty())) { + if (no_device_found) { if (tries == 0 || tries == MAX_REBOOT_TRIES) { if (tries) { fos << "\n\n"; @@ -10292,25 +10320,53 @@ int main(int argc, char **argv) { #if defined(__linux__) || defined(__APPLE__) printer(dr_vidpid_bootrom_cant_connect, " appears to be in BOOTSEL mode, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); - printer(dr_vidpid_stdio_usb_cant_connect, - " appears to have a USB serial connection, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); + printer(dr_vidpid_usb_reset_cant_connect, + " appears to have a USB reset interface, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); + printer(dr_vidpid_debugprobe_cant_connect, + " appears to be a DebugProbe, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); + printer(dr_vidpid_cant_connect, + " was tried, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); #else printer(dr_vidpid_bootrom_cant_connect, " appears to be in BOOTSEL mode, but picotool was unable to connect. You may need to install a driver via Zadig. See Zadig in the README (https://github.com/raspberrypi/picotool#zadig) for more information"); - printer(dr_vidpid_stdio_usb_cant_connect, - " appears to have a USB serial connection, but picotool was unable to connect."); + printer(dr_vidpid_usb_reset_cant_connect, + " appears to have a USB reset interface, but picotool was unable to connect."); + printer(dr_vidpid_debugprobe_cant_connect, + " appears to be a DebugProbe, but picotool was unable to connect."); + printer(dr_vidpid_cant_connect, + " was tried, but picotool was unable to connect."); #endif printer(dr_vidpid_picoprobe, " appears to be an RP-series PicoProbe device not in BOOTSEL mode."); printer(dr_vidpid_micropython, " appears to be an RP-series MicroPython device not in BOOTSEL mode."); + printer(dr_vidpid_circuitpython, + " appears to be an RP-series CircuitPython device not in BOOTSEL mode."); + printer(dr_vidpid_debugprobe, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it in order to run the command."); + if (settings.target_debugprobe) { + printer(dr_vidpid_debugprobe_no_reset, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but it does not have a USB reset interface so must be manually entered into BOOTSEL mode."); + } else { + printer(dr_vidpid_debugprobe_no_reset, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode."); + } if (selected_cmd->force_requires_pre_reboot()) { - printer(dr_vidpid_stdio_usb, - " appears to have a USB serial connection, so consider -f (or -F) to force reboot in order to run the command."); + printer(dr_vidpid_usb_reset, + " appears to have a USB reset interface, so consider -f (or -F) to force reboot in order to run the command."); } else { // special case message for what is actually just reboot (the only command that doesn't require reboot first) - printer(dr_vidpid_stdio_usb, - " appears to have a USB serial connection, so consider -f to force the reboot."); + printer(dr_vidpid_usb_reset, + " appears to have a USB reset interface, so consider -f to force the reboot."); + } + if (settings.only_force) { + if (settings.target_debugprobe) { + printer(dr_vidpid_bootrom_ok, + " appears to be an RP-series device already in BOOTSEL mode, but --debugprobe was passed so this is only targetting DebugProbes not already in BOOTSEL mode."); + } else { + printer(dr_vidpid_bootrom_ok, + " appears to be an RP-series device already in BOOTSEL mode, but --only-force was passed so this is only targetting devices not already in BOOTSEL mode."); + } } rc = ERROR_NO_DEVICE; } else { @@ -10319,13 +10375,13 @@ int main(int argc, char **argv) { } } else if (supported == cmd::device_support::one) { if (devices[dr_vidpid_bootrom_ok].size() > 1 || - (devices[dr_vidpid_bootrom_ok].empty() && devices[dr_vidpid_stdio_usb].size() > 1)) { + (devices[dr_vidpid_bootrom_ok].empty() && devices[dr_vidpid_usb_reset].size() > 1)) { fail(ERROR_NOT_POSSIBLE, "Command requires a single RP-series device to be targeted."); } - if (!devices[dr_vidpid_bootrom_ok].empty()) { + if (!devices[dr_vidpid_bootrom_ok].empty() && !settings.only_force) { settings.force = false; // we have a device, so we're not forcing } - } else if (supported == cmd::device_support::zero_or_more && settings.force && !devices[dr_vidpid_bootrom_ok].empty()) { + } else if (supported == cmd::device_support::zero_or_more && settings.force && !devices[dr_vidpid_bootrom_ok].empty() && !settings.only_force) { // we have usable devices, so lets use them without force settings.force = false; } @@ -10337,15 +10393,15 @@ int main(int argc, char **argv) { } if (!rc) { if (settings.force && ctx) { // actually ctx should never be null as we are targeting device if force is set, but still - if (devices[dr_vidpid_stdio_usb].size() != 1 && !tries) { + if (devices[dr_vidpid_usb_reset].size() != 1 && !tries) { fail(ERROR_NOT_POSSIBLE, "Forced command requires a single rebootable RP-series device to be targeted."); } if (selected_cmd->force_requires_pre_reboot()) { if (!tries) { // we reboot into BOOTSEL mode and disable MSC interface (the 1 here) - auto &to_reboot = std::get<1>(devices[dr_vidpid_stdio_usb][0]); - auto &to_reboot_handle = std::get<2>(devices[dr_vidpid_stdio_usb][0]); + auto &to_reboot = std::get<1>(devices[dr_vidpid_usb_reset][0]); + auto &to_reboot_handle = std::get<2>(devices[dr_vidpid_usb_reset][0]); unsigned int disable_mask = 1; // disable MSC interface #if defined(_WIN32) { @@ -10393,6 +10449,8 @@ int main(int argc, char **argv) { // again is to assume it has the same serial number. settings.address = -1; settings.bus = -1; + // also clear only_force, as the device should now be rebooting to BOOTSEL mode + settings.only_force = false; if (settings.pid != -1 || settings.vid != -1) { // vid/pid filtering was enabled, but may change in BOOTSEL mode, so needs to be disabled if (settings.ser.empty()) { diff --git a/picoboot_connection/picoboot_connection.c b/picoboot_connection/picoboot_connection.c index 60370c32..9bd3d5b5 100644 --- a/picoboot_connection/picoboot_connection.c +++ b/picoboot_connection/picoboot_connection.c @@ -66,6 +66,20 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d definitely_exclusive = false; *dev_handle = NULL; *chip = unknown; + bool custom_vid_pid = !(vid < 0 && pid < 0); + bool checking_for_debugprobe = false; + if (vid <= 0 || vid == VENDOR_ID_RASPBERRY_PI) { // no filtering is not custom_vid_pid + if (pid < 0 + || pid == PRODUCT_ID_RP2040_USBBOOT + || pid == PRODUCT_ID_RP2350_USBBOOT + ) { + // Don't treat passing BOOTSEL vid/pid, or VENDOR_ID_RASPBERRY_PI + // with no pid, as a custom vid/pid + custom_vid_pid = false; + } else if (vid == VENDOR_ID_RASPBERRY_PI && pid == PRODUCT_ID_DEBUGPROBE) { + checking_for_debugprobe = true; + } + } int ret = libusb_get_device_descriptor(device, &desc); enum picoboot_device_result res = dr_vidpid_unknown; if (ret && verbose) { @@ -75,7 +89,19 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d if (pid >= 0) { bool match_vid = (vid < 0 ? VENDOR_ID_RASPBERRY_PI : (unsigned int)vid) == desc.idVendor; bool match_pid = pid == desc.idProduct; - if (!(match_vid && match_pid)) { + if (checking_for_debugprobe) { + // Special case - if you pass --debugprobe but there is an RP2040 in bootsel mode, + // treat that as a match + if (!match_vid || !(match_pid || desc.idProduct == PRODUCT_ID_RP2040_USBBOOT)) { + return dr_vidpid_unknown; + } else { + *chip = rp2040; + if (desc.idProduct == PRODUCT_ID_RP2040_USBBOOT) { + // No longer checking for debugprobe, as this is an RP2040 in bootsel mode + checking_for_debugprobe = false; + } + } + } else if (!(match_vid && match_pid)) { return dr_vidpid_unknown; } } else if (vid != 0) { // ignore vid/pid filtering if no pid and vid == 0 @@ -87,13 +113,19 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d return dr_vidpid_micropython; case PRODUCT_ID_PICOPROBE: return dr_vidpid_picoprobe; + case PRODUCT_ID_CIRCUITPYTHON: + return dr_vidpid_circuitpython; + case PRODUCT_ID_DEBUGPROBE: + *chip = rp2040; + res = dr_vidpid_debugprobe; + break; case PRODUCT_ID_RP2040_STDIO_USB: *chip = rp2040; - res = dr_vidpid_stdio_usb; + res = dr_vidpid_usb_reset; break; case PRODUCT_ID_STDIO_USB: *chip = rp2350; - res = dr_vidpid_stdio_usb; + res = dr_vidpid_usb_reset; break; case PRODUCT_ID_RP2040_USBBOOT: *chip = rp2040; @@ -120,40 +152,53 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d if (vid == 0 || strlen(ser) != 0) { // didn't check vid or ser, so treat as unknown return dr_vidpid_unknown; - } else if (res == dr_vidpid_stdio_usb) { - return dr_vidpid_stdio_usb_cant_connect; - } else { + } else if (res == dr_vidpid_usb_reset) { + return dr_vidpid_usb_reset_cant_connect; + } else if (res == dr_vidpid_debugprobe) { + return dr_vidpid_debugprobe_cant_connect; + } else if (*chip != unknown) { return dr_vidpid_bootrom_cant_connect; + } else { + return dr_vidpid_cant_connect; } } } - if (!ret && res == dr_vidpid_stdio_usb) { + // Check USB serial number for usb_reset devices, or unknown devices with custom vid/pid + // (this won't hit the issue where RP2040 BOOTSEL USB serial number is not unique, as it + // cannot be white-labelled, so cannot have custom vid/pid) + if (!ret && (res == dr_vidpid_usb_reset || (res == dr_vidpid_unknown && custom_vid_pid))) { if (strlen(ser) != 0) { - // Check USB serial number char ser_str[128]; libusb_get_string_descriptor_ascii(*dev_handle, desc.iSerialNumber, (unsigned char*)ser_str, sizeof(ser_str)); if (strcmp(ser, ser_str)) { return dr_vidpid_unknown; - } else { - return res; } - } else { - return res; } } - // Runtime reset interface with thirdparty VID + // Runtime reset interface detection if (!ret) { for (int i = 0; i < config->bNumInterfaces; i++) { if (config->interface[i].altsetting[0].bInterfaceClass == 0xff && config->interface[i].altsetting[0].bInterfaceSubClass == RESET_INTERFACE_SUBCLASS && config->interface[i].altsetting[0].bInterfaceProtocol == RESET_INTERFACE_PROTOCOL) { - return dr_vidpid_stdio_usb; + if (res == dr_vidpid_unknown) { + return dr_vidpid_usb_reset; + } else { + // May already be setup, e.g dr_vidpid_debugprobe + return res; + } } } + + if (res == dr_vidpid_debugprobe || checking_for_debugprobe) { + // DebugProbe with no reset interface + return dr_vidpid_debugprobe_no_reset; + } } + // Picoboot interface detection if (!ret) { if (config->bNumInterfaces == 1) { interface = 0; diff --git a/picoboot_connection/picoboot_connection.h b/picoboot_connection/picoboot_connection.h index 31ee5b2e..0ca4aa1d 100644 --- a/picoboot_connection/picoboot_connection.h +++ b/picoboot_connection/picoboot_connection.h @@ -16,13 +16,15 @@ #include "boot/picoboot.h" #include "model.h" -#define VENDOR_ID_RASPBERRY_PI 0x2e8au -#define PRODUCT_ID_RP2040_USBBOOT 0x0003u -#define PRODUCT_ID_PICOPROBE 0x0004u -#define PRODUCT_ID_MICROPYTHON 0x0005u -#define PRODUCT_ID_STDIO_USB 0x0009u +#define VENDOR_ID_RASPBERRY_PI 0x2e8au +#define PRODUCT_ID_RP2040_USBBOOT 0x0003u +#define PRODUCT_ID_PICOPROBE 0x0004u +#define PRODUCT_ID_MICROPYTHON 0x0005u +#define PRODUCT_ID_STDIO_USB 0x0009u #define PRODUCT_ID_RP2040_STDIO_USB 0x000au -#define PRODUCT_ID_RP2350_USBBOOT 0x000fu +#define PRODUCT_ID_CIRCUITPYTHON 0x000bu +#define PRODUCT_ID_DEBUGPROBE 0x000cu +#define PRODUCT_ID_RP2350_USBBOOT 0x000fu #ifdef __cplusplus extern "C" { @@ -33,11 +35,16 @@ enum picoboot_device_result { dr_vidpid_bootrom_no_interface, dr_vidpid_bootrom_cant_connect, dr_vidpid_micropython, + dr_vidpid_circuitpython, dr_vidpid_picoprobe, + dr_vidpid_debugprobe, + dr_vidpid_debugprobe_cant_connect, + dr_vidpid_debugprobe_no_reset, dr_vidpid_unknown, dr_error, - dr_vidpid_stdio_usb, - dr_vidpid_stdio_usb_cant_connect, + dr_vidpid_usb_reset, + dr_vidpid_usb_reset_cant_connect, + dr_vidpid_cant_connect, }; From 4e0a138671b8cb2890434c6bbf685d021338a1b4 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 23 Jul 2026 13:33:01 +0100 Subject: [PATCH 2/7] Rearrange and comment everything better Also apply suggestions from code review --- main.cpp | 78 +++++++++++++---------- picoboot_connection/picoboot_connection.c | 19 ++++-- picoboot_connection/picoboot_connection.h | 43 ++++++++----- 3 files changed, 85 insertions(+), 55 deletions(-) diff --git a/main.cpp b/main.cpp index d60b998a..b4e2d9f8 100644 --- a/main.cpp +++ b/main.cpp @@ -714,10 +714,10 @@ auto device_selection = (option("--pid") & integer("pid").set(settings.pid)) % "Filter by product id" + (option("--ser") & value("ser").set(settings.ser)) % "Filter by serial number" + option("--rp2040").set(settings.force_rp2040) % "Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems" - + option("--debugprobe").set(settings.target_debugprobe) % "Force a DebugProbe device version >2.3.1 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" + + option("--debugprobe").set(settings.target_debugprobe) % "Force a DebugProbe device using firmware version >2.3.1 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" + 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" + 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 USB drive mounted" - + option("--only-force").set(settings.only_force) % "Same as --force, but will only target devices not currently in BOOTSEL mode" + + option("--only-force").set(settings.only_force) % "Same as --force, but will ignore devices already in BOOTSEL mode" + (option("--bootsel-led") & integer("gpio").set(settings.led)) % "Specify the GPIO for the BOOTSEL activity LED to flash (default " #if DEFAULT_BOOTSEL_LED < 0 @@ -10302,7 +10302,7 @@ int main(int argc, char **argv) { fos << " You may need to install a driver via Zadig. See Zadig in the README (https://github.com/raspberrypi/picotool#zadig) for more information."; } #endif - fos << " It is possible the device is not responding, and will have to be manually entered into BOOTSEL mode.\n"; + fos << " It is possible the device is not responding, and will have to be manually put into BOOTSEL mode.\n"; had_note = true; // suppress "but:" in this case } fos << "\n"; @@ -10317,11 +10317,36 @@ int main(int argc, char **argv) { fos << bus_device_string(std::get<1>(d), std::get<0>(d)) << description << "\n"; } }; + + // Devices missing required interfaces, so cannot be used by picotool + if (settings.vid != 0) { // if vid=0, vid/pid filtering is skipped, so this is returned for every accessible device (e.g. every USB device with sudo) + printer(dr_vidpid_bootrom_no_interface, + " was tried, but it does not have a PICOBOOT interface."); + } + if (settings.target_debugprobe) { + printer(dr_vidpid_debugprobe_no_reset, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but it does not have a USB reset interface so must be manually put into BOOTSEL mode."); + } else { + printer(dr_vidpid_debugprobe_no_reset, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode."); + } + printer(dr_vidpid_stdio_usb_no_reset, + " appears to have a USB serial connection, but it does not have a USB reset interface so must be manually put into BOOTSEL mode."); + if (selected_cmd->force_requires_pre_reboot()) { + printer(dr_vidpid_usb_reset, + " appears to have a USB reset interface, so consider -f (or -F) to force reboot in order to run the command."); + } else { + // special case message for what is actually just reboot (the only command that doesn't require reboot first) + printer(dr_vidpid_usb_reset, + " appears to have a USB reset interface, so consider -f to force the reboot."); + } + + // Devices failed to open (e.g. requires sudo) #if defined(__linux__) || defined(__APPLE__) printer(dr_vidpid_bootrom_cant_connect, " appears to be in BOOTSEL mode, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); - printer(dr_vidpid_usb_reset_cant_connect, - " appears to have a USB reset interface, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); + printer(dr_vidpid_stdio_usb_cant_connect, + " appears to have a USB serial connection, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); printer(dr_vidpid_debugprobe_cant_connect, " appears to be a DebugProbe, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); printer(dr_vidpid_cant_connect, @@ -10329,44 +10354,31 @@ int main(int argc, char **argv) { #else printer(dr_vidpid_bootrom_cant_connect, " appears to be in BOOTSEL mode, but picotool was unable to connect. You may need to install a driver via Zadig. See Zadig in the README (https://github.com/raspberrypi/picotool#zadig) for more information"); - printer(dr_vidpid_usb_reset_cant_connect, - " appears to have a USB reset interface, but picotool was unable to connect."); + printer(dr_vidpid_stdio_usb_cant_connect, + " appears to have a USB serial connection, but picotool was unable to connect."); printer(dr_vidpid_debugprobe_cant_connect, " appears to be a DebugProbe, but picotool was unable to connect."); printer(dr_vidpid_cant_connect, " was tried, but picotool was unable to connect."); #endif - printer(dr_vidpid_picoprobe, - " appears to be an RP-series PicoProbe device not in BOOTSEL mode."); + + // Other known pids + printer(dr_vidpid_debugprobe, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it in order to run the command."); printer(dr_vidpid_micropython, " appears to be an RP-series MicroPython device not in BOOTSEL mode."); printer(dr_vidpid_circuitpython, " appears to be an RP-series CircuitPython device not in BOOTSEL mode."); - printer(dr_vidpid_debugprobe, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it in order to run the command."); + printer(dr_vidpid_debugprobe_old, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode, running old firmware - you should manually put it into BOOTSEL mode and update the firmware (https://github.com/raspberrypi/debugprobe/releases)."); + + // Devices in BOOTSEL if (settings.target_debugprobe) { - printer(dr_vidpid_debugprobe_no_reset, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but it does not have a USB reset interface so must be manually entered into BOOTSEL mode."); - } else { - printer(dr_vidpid_debugprobe_no_reset, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode."); - } - if (selected_cmd->force_requires_pre_reboot()) { - printer(dr_vidpid_usb_reset, - " appears to have a USB reset interface, so consider -f (or -F) to force reboot in order to run the command."); - } else { - // special case message for what is actually just reboot (the only command that doesn't require reboot first) - printer(dr_vidpid_usb_reset, - " appears to have a USB reset interface, so consider -f to force the reboot."); - } - if (settings.only_force) { - if (settings.target_debugprobe) { - printer(dr_vidpid_bootrom_ok, - " appears to be an RP-series device already in BOOTSEL mode, but --debugprobe was passed so this is only targetting DebugProbes not already in BOOTSEL mode."); - } else { - printer(dr_vidpid_bootrom_ok, - " appears to be an RP-series device already in BOOTSEL mode, but --only-force was passed so this is only targetting devices not already in BOOTSEL mode."); - } + printer(dr_vidpid_bootrom_ok, + " appears to be an RP-series device already in BOOTSEL mode, but --debugprobe was passed so this is only targetting DebugProbes not already in BOOTSEL mode."); + } else if (settings.only_force) { + printer(dr_vidpid_bootrom_ok, + " appears to be an RP-series device already in BOOTSEL mode, but --only-force was passed so this is only targetting devices not already in BOOTSEL mode."); } rc = ERROR_NO_DEVICE; } else { diff --git a/picoboot_connection/picoboot_connection.c b/picoboot_connection/picoboot_connection.c index 9bd3d5b5..f07bc4b6 100644 --- a/picoboot_connection/picoboot_connection.c +++ b/picoboot_connection/picoboot_connection.c @@ -68,7 +68,7 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d *chip = unknown; bool custom_vid_pid = !(vid < 0 && pid < 0); bool checking_for_debugprobe = false; - if (vid <= 0 || vid == VENDOR_ID_RASPBERRY_PI) { // no filtering is not custom_vid_pid + if (vid <= 0 || vid == VENDOR_ID_RASPBERRY_PI) { // no filtering if not custom_vid_pid if (pid < 0 || pid == PRODUCT_ID_RP2040_USBBOOT || pid == PRODUCT_ID_RP2350_USBBOOT @@ -111,8 +111,8 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d switch (desc.idProduct) { case PRODUCT_ID_MICROPYTHON: return dr_vidpid_micropython; - case PRODUCT_ID_PICOPROBE: - return dr_vidpid_picoprobe; + case PRODUCT_ID_DEBUGPROBE_OLD: + return dr_vidpid_debugprobe_old; case PRODUCT_ID_CIRCUITPYTHON: return dr_vidpid_circuitpython; case PRODUCT_ID_DEBUGPROBE: @@ -152,11 +152,11 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d if (vid == 0 || strlen(ser) != 0) { // didn't check vid or ser, so treat as unknown return dr_vidpid_unknown; - } else if (res == dr_vidpid_usb_reset) { - return dr_vidpid_usb_reset_cant_connect; - } else if (res == dr_vidpid_debugprobe) { + } else if (res == dr_vidpid_usb_reset) { // only set by having STDIO_USB PIDs + return dr_vidpid_stdio_usb_cant_connect; + } else if (res == dr_vidpid_debugprobe || checking_for_debugprobe) { return dr_vidpid_debugprobe_cant_connect; - } else if (*chip != unknown) { + } else if (*chip != unknown) { // set by the two cases caught above, plus BOOTSEL PIDs return dr_vidpid_bootrom_cant_connect; } else { return dr_vidpid_cant_connect; @@ -196,6 +196,11 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d // DebugProbe with no reset interface return dr_vidpid_debugprobe_no_reset; } + + if (res == dr_vidpid_usb_reset) { + // STDIO_USB with no reset interface + return dr_vidpid_stdio_usb_no_reset; + } } // Picoboot interface detection diff --git a/picoboot_connection/picoboot_connection.h b/picoboot_connection/picoboot_connection.h index 0ca4aa1d..5e2f920e 100644 --- a/picoboot_connection/picoboot_connection.h +++ b/picoboot_connection/picoboot_connection.h @@ -18,7 +18,7 @@ #define VENDOR_ID_RASPBERRY_PI 0x2e8au #define PRODUCT_ID_RP2040_USBBOOT 0x0003u -#define PRODUCT_ID_PICOPROBE 0x0004u +#define PRODUCT_ID_DEBUGPROBE_OLD 0x0004u #define PRODUCT_ID_MICROPYTHON 0x0005u #define PRODUCT_ID_STDIO_USB 0x0009u #define PRODUCT_ID_RP2040_STDIO_USB 0x000au @@ -31,20 +31,33 @@ extern "C" { #endif enum picoboot_device_result { - dr_vidpid_bootrom_ok, - dr_vidpid_bootrom_no_interface, - dr_vidpid_bootrom_cant_connect, - dr_vidpid_micropython, - dr_vidpid_circuitpython, - dr_vidpid_picoprobe, - dr_vidpid_debugprobe, - dr_vidpid_debugprobe_cant_connect, - dr_vidpid_debugprobe_no_reset, - dr_vidpid_unknown, - dr_error, - dr_vidpid_usb_reset, - dr_vidpid_usb_reset_cant_connect, - dr_vidpid_cant_connect, + // For these comments, selected vid/pid means either BOOTSEL vid/pid, stdio_usb vid/pid, or vid/pid was passed to picoboot_open_device + // (e.g. by passing --vid, --pid, or --debugprobe) + + // Devices that can be used by picotool + dr_vidpid_bootrom_ok, // selected vid/pid has a picoboot interface (i.e. device in BOOTSEL) + dr_vidpid_usb_reset, // selected vid/pid has a USB reset interface (e.g. stdio_usb, or custom vid/pid) + + // Devices missing required interfaces, so cannot be used by picotool + dr_vidpid_bootrom_no_interface, // selected vid/pid has no picoboot interface (and no reset interface as that is checked for first) + dr_vidpid_debugprobe_no_reset, // debugprobe vid/pid, but no reset interface (e.g. firmware <=2.3.1) + dr_vidpid_stdio_usb_no_reset, // stdio_usb vid/pid, but no reset interface (e.g. PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE=0) + + // Devices failed to open (e.g. requires sudo) + dr_vidpid_bootrom_cant_connect, // bootrom vid/pid + dr_vidpid_debugprobe_cant_connect, // debugprobe vid/pid + dr_vidpid_stdio_usb_cant_connect, // stdio_usb vid/pid + dr_vidpid_cant_connect, // custom vid/pid + + // Other known pids for no device found messages + dr_vidpid_debugprobe, // debugprobe vid/pid, with a USB reset interface - only returned when not searching for debugprobe vid/pid + dr_vidpid_micropython, // micropython vid/pid + dr_vidpid_circuitpython, // circuitpython vid/pid + dr_vidpid_debugprobe_old, // old debugprobe vid/pid (v1.0 and v1.0.1) + + // Ignored devices + dr_vidpid_unknown, // unknown device (e.g. no vid/pid/serial match) + dr_error, // an error occurred (e.g. libusb_get_device_descriptor or libusb_get_active_config_descriptor failed) }; From 8c367a1ecd842663ff02baeedbced235f17c2598 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 23 Jul 2026 13:35:22 +0100 Subject: [PATCH 3/7] Update readme --- README.md | 168 +++++++++++++++++++++++++++--------------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index ccf500f2..2ed16bec 100644 --- a/README.md +++ b/README.md @@ -131,9 +131,9 @@ TARGET SELECTION: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -144,7 +144,7 @@ TARGET SELECTION: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -254,9 +254,9 @@ TARGET SELECTION: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -267,7 +267,7 @@ TARGET SELECTION: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -375,9 +375,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -388,7 +388,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -459,9 +459,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -472,7 +472,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -550,9 +550,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -563,7 +563,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -617,9 +617,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -630,7 +630,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -716,9 +716,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -729,7 +729,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -917,9 +917,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -930,7 +930,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1198,9 +1198,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -1211,7 +1211,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1320,9 +1320,9 @@ TARGET SELECTION: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -1333,7 +1333,7 @@ TARGET SELECTION: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1399,9 +1399,9 @@ TARGET SELECTION: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -1412,7 +1412,7 @@ TARGET SELECTION: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1465,9 +1465,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -1478,7 +1478,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1526,9 +1526,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -1539,7 +1539,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1629,9 +1629,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -1642,7 +1642,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1711,9 +1711,9 @@ TARGET SELECTION: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -1724,7 +1724,7 @@ TARGET SELECTION: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -1933,9 +1933,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -1946,7 +1946,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -2010,9 +2010,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -2023,7 +2023,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -2091,9 +2091,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -2104,7 +2104,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -2168,9 +2168,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -2181,7 +2181,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -2245,9 +2245,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -2258,7 +2258,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to @@ -2318,9 +2318,9 @@ OPTIONS: Assume the device is an RP2040 - this is only required when using a custom vid/pid with an RP2040 on Windows, and is ignored on other operating systems --debugprobe - Force a DebugProbe device version >2.3.1 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 + Force a DebugProbe device using firmware version >2.3.1 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 -f, --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 @@ -2331,7 +2331,7 @@ OPTIONS: a 'reboot') the device will be left connected and accessible to picotool, but without the USB drive mounted --only-force - Same as --force, but will only target devices not currently in BOOTSEL mode + Same as --force, but will ignore devices already in BOOTSEL mode --bootsel-led Specify the GPIO for the BOOTSEL activity LED to flash (default none, ignored by RP2350A-A2 in Arm mode) - only applicable if this command reboots the device to From 80b57a1aeee6e4f48be90df1e26c0ffcb6d50081 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 23 Jul 2026 14:15:44 +0100 Subject: [PATCH 4/7] Add picotool list command --- README.md | 58 ++++++++++++++++- main.cpp | 191 +++++++++++++++++++++++++++++++++--------------------- 2 files changed, 175 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 2ed16bec..9791bb7e 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ SYNOPSIS: picotool erase -p [device-selection] picotool erase -r [device-selection] picotool reboot [-a] [-u] [-g ] [-c ] [device-selection] + picotool list picotool seal [--quiet] [--verbose] [--hash] [--sign] [--clear] [--pin-xip-sram] [--no-squash] [-t ] [-o ] [-t ] [] [] [--major ] [--minor ] [--rollback [..]] @@ -56,6 +57,7 @@ COMMANDS: verify Check that the device contents match those in the file. erase Erase the program / memory stored in flash on the device. reboot Reboot the device + list List all connected RP-series devices seal Add final metadata to a binary, optionally including a hash and/or signature. encrypt Encrypt the program. partition Commands related to RP2350 Partition Tables @@ -71,7 +73,7 @@ Use "picotool help " for more info Note commands that aren't acting on files require a device in BOOTSEL mode to be connected. ## Links to documentation for `picotool` commands -[`info`](#info) [`config`](#config) [`load`](#load) [`save`](#save) [`verify`](#verify) [`erase`](#erase) [`reboot`](#reboot) [`seal`](#seal) [`encrypt`](#encrypt) [`partition`](#partition) [`uf2`](#uf2) [`otp`](#otp) [`coprodis`](#coprodis) [`link`](#link) [`bdev`](#bdev) +[`info`](#info) [`config`](#config) [`load`](#load) [`save`](#save) [`verify`](#verify) [`erase`](#erase) [`reboot`](#reboot) [`list`](#list) [`seal`](#seal) [`encrypt`](#encrypt) [`partition`](#partition) [`uf2`](#uf2) [`otp`](#otp) [`coprodis`](#coprodis) [`link`](#link) [`bdev`](#bdev) ## Building & Installing @@ -738,6 +740,60 @@ OPTIONS: The BOOTSEL activity LED is active low (ignored by RP2040 and RP2350-A4) ``` +## list + +`list` allows you to list all connected RP-series devices + +```text +$ picotool help list +LIST: + List all connected RP-series devices + +SYNOPSIS: + picotool list +``` + +For example, with a DebugProbe and an RP2350 connected: + +```text +$ picotool list +Detected 2 RP-series devices: + + RP2350 device at bus 1, address 25 appears to have a USB reset interface, so consider -f (or + -F) to force reboot in order to run the command. + + RP2040 device at bus 1, address 27 appears to be an RP-series DebugProbe device not in + BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it + in order to run the command. + +``` + +Then after running `picotool reboot -f -u`: + +```text +$ picotool list +Detected 2 RP-series devices: + + RP2350 device at bus 1, address 29 appears to be an RP-series device in BOOTSEL mode. + + RP2040 device at bus 1, address 27 appears to be an RP-series DebugProbe device not in + BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it + in order to run the command. + +``` + +And also running `picotool reboot --debugprobe -u`: + +```text +$ picotool list +Detected 2 RP-series devices: + + RP2040 device at bus 1, address 30 appears to be an RP-series device in BOOTSEL mode. + + RP2350 device at bus 1, address 29 appears to be an RP-series device in BOOTSEL mode. + +``` + ## seal `seal` allows you to sign and/or hash a binary to run on RP2350. diff --git a/main.cpp b/main.cpp index b4e2d9f8..d7d07540 100644 --- a/main.cpp +++ b/main.cpp @@ -519,7 +519,7 @@ struct bdev_fs : public cli::value_base { struct cmd { explicit cmd(string name) : _name(std::move(name)) {} virtual ~cmd() = default; - enum device_support { none, one, zero_or_more }; + enum device_support { none, one, zero_or_more, any }; virtual group get_cli() = 0; virtual string get_doc() const = 0; virtual device_support get_device_support() { return one; } @@ -1766,6 +1766,23 @@ struct version_command : public cmd { }; #if HAS_LIBUSB +struct list_command : public cmd { + list_command() : cmd("list") {} + bool execute(device_map &devices) override; + + device_support get_device_support() override { + return device_support::any; + } + + group get_cli() override { + return group(); + } + + string get_doc() const override { + return "List all connected RP-series devices"; + } +}; + struct reboot_command : public cmd { bool quiet; reboot_command() : cmd("reboot") {} @@ -1806,6 +1823,7 @@ vector> commands { std::shared_ptr(new verify_command()), std::shared_ptr(new erase_command()), reboot_cmd, + std::shared_ptr(new list_command()), #endif #if HAS_MBEDTLS std::shared_ptr(new seal_command()), @@ -10144,6 +10162,103 @@ bool reboot_command::execute(device_map &devices) { } return true; } + +static void print_device_info(device_map &devices, bool had_note=false, bool include_bootsel=false) { + auto printer = [&](enum picoboot_device_result r, const string &description) { + if (!had_note && !devices[r].empty()) { + fos << "\nbut:\n\n"; + had_note = true; + } + for (auto d : devices[r]) { + fos << bus_device_string(std::get<1>(d), std::get<0>(d)) << description << "\n"; + } + }; + + // Devices in BOOTSEL + if (settings.target_debugprobe) { + printer(dr_vidpid_bootrom_ok, + " appears to be an RP-series device already in BOOTSEL mode, but --debugprobe was passed so this is only targetting DebugProbes not already in BOOTSEL mode."); + } else if (settings.only_force) { + printer(dr_vidpid_bootrom_ok, + " appears to be an RP-series device already in BOOTSEL mode, but --only-force was passed so this is only targetting devices not already in BOOTSEL mode."); + } else if (include_bootsel) { + printer(dr_vidpid_bootrom_ok, + " appears to be an RP-series device in BOOTSEL mode."); + } + + // Devices missing required interfaces, so cannot be used by picotool + if (settings.vid != 0) { // if vid=0, vid/pid filtering is skipped, so this is returned for every accessible device (e.g. every USB device with sudo) + printer(dr_vidpid_bootrom_no_interface, + " was tried, but it does not have a PICOBOOT interface."); + } + if (settings.target_debugprobe) { + printer(dr_vidpid_debugprobe_no_reset, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but it does not have a USB reset interface so must be manually put into BOOTSEL mode."); + } else { + printer(dr_vidpid_debugprobe_no_reset, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode."); + } + printer(dr_vidpid_stdio_usb_no_reset, + " appears to have a USB serial connection, but it does not have a USB reset interface so must be manually put into BOOTSEL mode."); + if (selected_cmd->force_requires_pre_reboot()) { + printer(dr_vidpid_usb_reset, + " appears to have a USB reset interface, so consider -f (or -F) to force reboot in order to run the command."); + } else { + // special case message for what is actually just reboot (the only command that doesn't require reboot first) + printer(dr_vidpid_usb_reset, + " appears to have a USB reset interface, so consider -f to force the reboot."); + } + + // Devices failed to open (e.g. requires sudo) +#if defined(__linux__) || defined(__APPLE__) + printer(dr_vidpid_bootrom_cant_connect, + " appears to be in BOOTSEL mode, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); + printer(dr_vidpid_stdio_usb_cant_connect, + " appears to have a USB serial connection, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); + printer(dr_vidpid_debugprobe_cant_connect, + " appears to be a DebugProbe, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); + printer(dr_vidpid_cant_connect, + " was tried, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); +#else + printer(dr_vidpid_bootrom_cant_connect, + " appears to be in BOOTSEL mode, but picotool was unable to connect. You may need to install a driver via Zadig. See Zadig in the README (https://github.com/raspberrypi/picotool#zadig) for more information"); + printer(dr_vidpid_stdio_usb_cant_connect, + " appears to have a USB serial connection, but picotool was unable to connect."); + printer(dr_vidpid_debugprobe_cant_connect, + " appears to be a DebugProbe, but picotool was unable to connect."); + printer(dr_vidpid_cant_connect, + " was tried, but picotool was unable to connect."); +#endif + + // Other known pids + printer(dr_vidpid_debugprobe, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it in order to run the command."); + printer(dr_vidpid_micropython, + " appears to be an RP-series MicroPython device not in BOOTSEL mode."); + printer(dr_vidpid_circuitpython, + " appears to be an RP-series CircuitPython device not in BOOTSEL mode."); + printer(dr_vidpid_debugprobe_old, + " appears to be an RP-series DebugProbe device not in BOOTSEL mode, running old firmware - you should manually put it into BOOTSEL mode and update the firmware (https://github.com/raspberrypi/debugprobe/releases)."); +} + +bool list_command::execute(device_map &devices) { + int num_devices = 0; + + for (auto devs = devices.begin(); devs != devices.end(); devs++) { + if (devs->first != dr_vidpid_unknown) { + num_devices += devs->second.size(); + } + } + + fos << "Detected " << num_devices << " RP-series devices:\n\n"; + fos.first_column(2); + fos.hanging_indent(4); + fos.paragraph_spacing(1); + fos.min_paragraph_lines_for_spacing(1); + print_device_info(devices, true, true); + + return false; +} #endif #if defined(_WIN32) @@ -10308,78 +10423,7 @@ int main(int argc, char **argv) { fos << "\n"; fos.first_column(0); fos.hanging_indent(4); - auto printer = [&](enum picoboot_device_result r, const string &description) { - if (!had_note && !devices[r].empty()) { - fos << "\nbut:\n\n"; - had_note = true; - } - for (auto d : devices[r]) { - fos << bus_device_string(std::get<1>(d), std::get<0>(d)) << description << "\n"; - } - }; - - // Devices missing required interfaces, so cannot be used by picotool - if (settings.vid != 0) { // if vid=0, vid/pid filtering is skipped, so this is returned for every accessible device (e.g. every USB device with sudo) - printer(dr_vidpid_bootrom_no_interface, - " was tried, but it does not have a PICOBOOT interface."); - } - if (settings.target_debugprobe) { - printer(dr_vidpid_debugprobe_no_reset, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but it does not have a USB reset interface so must be manually put into BOOTSEL mode."); - } else { - printer(dr_vidpid_debugprobe_no_reset, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode."); - } - printer(dr_vidpid_stdio_usb_no_reset, - " appears to have a USB serial connection, but it does not have a USB reset interface so must be manually put into BOOTSEL mode."); - if (selected_cmd->force_requires_pre_reboot()) { - printer(dr_vidpid_usb_reset, - " appears to have a USB reset interface, so consider -f (or -F) to force reboot in order to run the command."); - } else { - // special case message for what is actually just reboot (the only command that doesn't require reboot first) - printer(dr_vidpid_usb_reset, - " appears to have a USB reset interface, so consider -f to force the reboot."); - } - - // Devices failed to open (e.g. requires sudo) - #if defined(__linux__) || defined(__APPLE__) - printer(dr_vidpid_bootrom_cant_connect, - " appears to be in BOOTSEL mode, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); - printer(dr_vidpid_stdio_usb_cant_connect, - " appears to have a USB serial connection, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); - printer(dr_vidpid_debugprobe_cant_connect, - " appears to be a DebugProbe, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); - printer(dr_vidpid_cant_connect, - " was tried, but picotool was unable to connect. Maybe try 'sudo' or check your permissions."); - #else - printer(dr_vidpid_bootrom_cant_connect, - " appears to be in BOOTSEL mode, but picotool was unable to connect. You may need to install a driver via Zadig. See Zadig in the README (https://github.com/raspberrypi/picotool#zadig) for more information"); - printer(dr_vidpid_stdio_usb_cant_connect, - " appears to have a USB serial connection, but picotool was unable to connect."); - printer(dr_vidpid_debugprobe_cant_connect, - " appears to be a DebugProbe, but picotool was unable to connect."); - printer(dr_vidpid_cant_connect, - " was tried, but picotool was unable to connect."); - #endif - - // Other known pids - printer(dr_vidpid_debugprobe, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it in order to run the command."); - printer(dr_vidpid_micropython, - " appears to be an RP-series MicroPython device not in BOOTSEL mode."); - printer(dr_vidpid_circuitpython, - " appears to be an RP-series CircuitPython device not in BOOTSEL mode."); - printer(dr_vidpid_debugprobe_old, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode, running old firmware - you should manually put it into BOOTSEL mode and update the firmware (https://github.com/raspberrypi/debugprobe/releases)."); - - // Devices in BOOTSEL - if (settings.target_debugprobe) { - printer(dr_vidpid_bootrom_ok, - " appears to be an RP-series device already in BOOTSEL mode, but --debugprobe was passed so this is only targetting DebugProbes not already in BOOTSEL mode."); - } else if (settings.only_force) { - printer(dr_vidpid_bootrom_ok, - " appears to be an RP-series device already in BOOTSEL mode, but --only-force was passed so this is only targetting devices not already in BOOTSEL mode."); - } + print_device_info(devices, had_note); rc = ERROR_NO_DEVICE; } else { // waiting for rebooted device to show up @@ -10400,6 +10444,7 @@ int main(int argc, char **argv) { fos.first_column(0); fos.hanging_indent(0); break; + case cmd::device_support::any: default: break; } From fc99539e9acd90d793ae9b1b9ea4ec15dd06f8a1 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 23 Jul 2026 14:32:10 +0100 Subject: [PATCH 5/7] Remove RP-series from no device messages chip_name (RP-series, RP2040 or RP2350) is printed at the start of the line anyway by bus_device_string Also reword some of the strings, so all strings explain how to put the device into BOOTSEL mode --- README.md | 18 ++++++++---------- main.cpp | 23 +++++++++-------------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 9791bb7e..21b67f88 100644 --- a/README.md +++ b/README.md @@ -759,12 +759,11 @@ For example, with a DebugProbe and an RP2350 connected: $ picotool list Detected 2 RP-series devices: - RP2350 device at bus 1, address 25 appears to have a USB reset interface, so consider -f (or + RP2350 device at bus 1, address 31 appears to have a USB reset interface, so consider -f (or -F) to force reboot in order to run the command. - RP2040 device at bus 1, address 27 appears to be an RP-series DebugProbe device not in - BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it - in order to run the command. + RP2040 device at bus 1, address 32 appears to be a DebugProbe with a USB reset interface, so + consider --debugprobe to force reboot it in order to run the command. ``` @@ -774,11 +773,10 @@ Then after running `picotool reboot -f -u`: $ picotool list Detected 2 RP-series devices: - RP2350 device at bus 1, address 29 appears to be an RP-series device in BOOTSEL mode. + RP2350 device at bus 1, address 33 appears to be in BOOTSEL mode. - RP2040 device at bus 1, address 27 appears to be an RP-series DebugProbe device not in - BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it - in order to run the command. + RP2040 device at bus 1, address 32 appears to be a DebugProbe with a USB reset interface, so + consider --debugprobe to force reboot it in order to run the command. ``` @@ -788,9 +786,9 @@ And also running `picotool reboot --debugprobe -u`: $ picotool list Detected 2 RP-series devices: - RP2040 device at bus 1, address 30 appears to be an RP-series device in BOOTSEL mode. + RP2040 device at bus 1, address 34 appears to be in BOOTSEL mode. - RP2350 device at bus 1, address 29 appears to be an RP-series device in BOOTSEL mode. + RP2350 device at bus 1, address 33 appears to be in BOOTSEL mode. ``` diff --git a/main.cpp b/main.cpp index d7d07540..88798316 100644 --- a/main.cpp +++ b/main.cpp @@ -10177,13 +10177,13 @@ static void print_device_info(device_map &devices, bool had_note=false, bool inc // Devices in BOOTSEL if (settings.target_debugprobe) { printer(dr_vidpid_bootrom_ok, - " appears to be an RP-series device already in BOOTSEL mode, but --debugprobe was passed so this is only targetting DebugProbes not already in BOOTSEL mode."); + " appears to already be in BOOTSEL mode, but --debugprobe was passed so this is only targetting DebugProbes not already in BOOTSEL mode."); } else if (settings.only_force) { printer(dr_vidpid_bootrom_ok, - " appears to be an RP-series device already in BOOTSEL mode, but --only-force was passed so this is only targetting devices not already in BOOTSEL mode."); + " appears to already be in BOOTSEL mode, but --only-force was passed so this is only targetting devices not already in BOOTSEL mode."); } else if (include_bootsel) { printer(dr_vidpid_bootrom_ok, - " appears to be an RP-series device in BOOTSEL mode."); + " appears to be in BOOTSEL mode."); } // Devices missing required interfaces, so cannot be used by picotool @@ -10191,13 +10191,8 @@ static void print_device_info(device_map &devices, bool had_note=false, bool inc printer(dr_vidpid_bootrom_no_interface, " was tried, but it does not have a PICOBOOT interface."); } - if (settings.target_debugprobe) { - printer(dr_vidpid_debugprobe_no_reset, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but it does not have a USB reset interface so must be manually put into BOOTSEL mode."); - } else { - printer(dr_vidpid_debugprobe_no_reset, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode."); - } + printer(dr_vidpid_debugprobe_no_reset, + " appears to be a DebugProbe without a USB reset interface, so must be manually put into BOOTSEL mode."); printer(dr_vidpid_stdio_usb_no_reset, " appears to have a USB serial connection, but it does not have a USB reset interface so must be manually put into BOOTSEL mode."); if (selected_cmd->force_requires_pre_reboot()) { @@ -10232,13 +10227,13 @@ static void print_device_info(device_map &devices, bool had_note=false, bool inc // Other known pids printer(dr_vidpid_debugprobe, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode, but with a USB reset interface, so consider --debugprobe to force reboot it in order to run the command."); + " appears to be a DebugProbe with a USB reset interface, so consider --debugprobe to force reboot it in order to run the command."); printer(dr_vidpid_micropython, - " appears to be an RP-series MicroPython device not in BOOTSEL mode."); + " appears to be running MicroPython, so must be manually put into BOOTSEL mode."); printer(dr_vidpid_circuitpython, - " appears to be an RP-series CircuitPython device not in BOOTSEL mode."); + " appears to be running CircuitPython, so must be manually put into BOOTSEL mode."); printer(dr_vidpid_debugprobe_old, - " appears to be an RP-series DebugProbe device not in BOOTSEL mode, running old firmware - you should manually put it into BOOTSEL mode and update the firmware (https://github.com/raspberrypi/debugprobe/releases)."); + " appears to be a DebugProbe running old firmware - you should manually put it into BOOTSEL mode and update the firmware (https://github.com/raspberrypi/debugprobe/releases)."); } bool list_command::execute(device_map &devices) { From 92b98541ac75476ad185be5333880fe426c8967a Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 24 Jul 2026 14:24:51 +0100 Subject: [PATCH 6/7] Fix serial number detection with multiple devices Also improve detection when passing known pids, to have same behaviour as if it were detected (e.g. passing --pid 0xa to only target RP2040 devices using stdio_usb) --- picoboot_connection/picoboot_connection.c | 113 ++++++++++++---------- picoboot_connection/picoboot_connection.h | 4 +- 2 files changed, 64 insertions(+), 53 deletions(-) diff --git a/picoboot_connection/picoboot_connection.c b/picoboot_connection/picoboot_connection.c index f07bc4b6..954a8849 100644 --- a/picoboot_connection/picoboot_connection.c +++ b/picoboot_connection/picoboot_connection.c @@ -59,6 +59,34 @@ unsigned int interface; unsigned int out_ep; unsigned int in_ep; +enum picoboot_device_result match_rpi_pid(chip_t *chip, int dev_pid) { + switch (dev_pid) { + case PRODUCT_ID_MICROPYTHON: + return dr_vidpid_micropython; + case PRODUCT_ID_DEBUGPROBE_OLD: + return dr_vidpid_debugprobe_old; + case PRODUCT_ID_CIRCUITPYTHON: + return dr_vidpid_circuitpython; + case PRODUCT_ID_DEBUGPROBE: + *chip = rp2040; + return dr_vidpid_debugprobe; + case PRODUCT_ID_RP2040_STDIO_USB: + *chip = rp2040; + return dr_vidpid_usb_reset; + case PRODUCT_ID_STDIO_USB: + *chip = rp2350; + return dr_vidpid_usb_reset; + case PRODUCT_ID_RP2040_USBBOOT: + *chip = rp2040; + return dr_vidpid_bootrom_ok; + case PRODUCT_ID_RP2350_USBBOOT: + *chip = rp2350; + return dr_vidpid_bootrom_ok; + default: + return dr_vidpid_unknown; + } +} + enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, chip_t *chip, int vid, int pid, const char* ser) { struct libusb_device_descriptor desc; struct libusb_config_descriptor *config; @@ -66,19 +94,9 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d definitely_exclusive = false; *dev_handle = NULL; *chip = unknown; - bool custom_vid_pid = !(vid < 0 && pid < 0); bool checking_for_debugprobe = false; - if (vid <= 0 || vid == VENDOR_ID_RASPBERRY_PI) { // no filtering if not custom_vid_pid - if (pid < 0 - || pid == PRODUCT_ID_RP2040_USBBOOT - || pid == PRODUCT_ID_RP2350_USBBOOT - ) { - // Don't treat passing BOOTSEL vid/pid, or VENDOR_ID_RASPBERRY_PI - // with no pid, as a custom vid/pid - custom_vid_pid = false; - } else if (vid == VENDOR_ID_RASPBERRY_PI && pid == PRODUCT_ID_DEBUGPROBE) { - checking_for_debugprobe = true; - } + if (vid == VENDOR_ID_RASPBERRY_PI && pid == PRODUCT_ID_DEBUGPROBE) { + checking_for_debugprobe = true; } int ret = libusb_get_device_descriptor(device, &desc); enum picoboot_device_result res = dr_vidpid_unknown; @@ -91,7 +109,8 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d bool match_pid = pid == desc.idProduct; if (checking_for_debugprobe) { // Special case - if you pass --debugprobe but there is an RP2040 in bootsel mode, - // treat that as a match + // treat that as a match so it returns dr_vidpid_bootrom_ok - it will not pick + // that anyway, if (!match_vid || !(match_pid || desc.idProduct == PRODUCT_ID_RP2040_USBBOOT)) { return dr_vidpid_unknown; } else { @@ -99,42 +118,31 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d if (desc.idProduct == PRODUCT_ID_RP2040_USBBOOT) { // No longer checking for debugprobe, as this is an RP2040 in bootsel mode checking_for_debugprobe = false; + res = dr_vidpid_bootrom_ok; } } - } else if (!(match_vid && match_pid)) { + } else if (!match_vid || !match_pid) { return dr_vidpid_unknown; + } else if (desc.idVendor == VENDOR_ID_RASPBERRY_PI) { + // Handle the case where --pid was passed, but it's a standard value + res = match_rpi_pid(chip, desc.idProduct); + if (res > dr_vidpid_debugprobe) { + // everything after dr_vidpid_debugprobe isn't handled in the rest of the function, + // so set back to unknown as it was selected and needs to be detected + // (e.g. micropython with a reset interface added) + res = dr_vidpid_unknown; + } } - } else if (vid != 0) { // ignore vid/pid filtering if no pid and vid == 0 - if (desc.idVendor != (vid < 0 ? VENDOR_ID_RASPBERRY_PI : (unsigned int)vid)) { + } else { // ignore vid/pid filtering if no pid and vid == 0 + if (vid != 0 && desc.idVendor != (vid < 0 ? VENDOR_ID_RASPBERRY_PI : (unsigned int)vid)) { return dr_vidpid_unknown; - } - switch (desc.idProduct) { - case PRODUCT_ID_MICROPYTHON: - return dr_vidpid_micropython; - case PRODUCT_ID_DEBUGPROBE_OLD: - return dr_vidpid_debugprobe_old; - case PRODUCT_ID_CIRCUITPYTHON: - return dr_vidpid_circuitpython; - case PRODUCT_ID_DEBUGPROBE: - *chip = rp2040; - res = dr_vidpid_debugprobe; - break; - case PRODUCT_ID_RP2040_STDIO_USB: - *chip = rp2040; - res = dr_vidpid_usb_reset; - break; - case PRODUCT_ID_STDIO_USB: - *chip = rp2350; - res = dr_vidpid_usb_reset; - break; - case PRODUCT_ID_RP2040_USBBOOT: - *chip = rp2040; - break; - case PRODUCT_ID_RP2350_USBBOOT: - *chip = rp2350; - break; - default: - return dr_vidpid_unknown; + } else if (desc.idVendor == VENDOR_ID_RASPBERRY_PI) { + res = match_rpi_pid(chip, desc.idProduct); + if (res > dr_vidpid_debugprobe) { + // everything after dr_vidpid_debugprobe isn't handled in the rest of the function, + // so return it now as it was not selected (e.g. micropython) + return res; + } } } ret = libusb_get_active_config_descriptor(device, &config); @@ -156,7 +164,7 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d return dr_vidpid_stdio_usb_cant_connect; } else if (res == dr_vidpid_debugprobe || checking_for_debugprobe) { return dr_vidpid_debugprobe_cant_connect; - } else if (*chip != unknown) { // set by the two cases caught above, plus BOOTSEL PIDs + } else if (res = dr_vidpid_bootrom_ok) { return dr_vidpid_bootrom_cant_connect; } else { return dr_vidpid_cant_connect; @@ -164,21 +172,22 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d } } - // Check USB serial number for usb_reset devices, or unknown devices with custom vid/pid - // (this won't hit the issue where RP2040 BOOTSEL USB serial number is not unique, as it - // cannot be white-labelled, so cannot have custom vid/pid) - if (!ret && (res == dr_vidpid_usb_reset || (res == dr_vidpid_unknown && custom_vid_pid))) { - if (strlen(ser) != 0) { + // Check USB serial number - skipped for BOOTSEL rp2040 vid/pid, as it is not unique, so flash ID is used instead + bool usb_ser_match = strlen(ser) == 0; // true if no serial number passed, false otherwise + if (!ret && !(res == dr_vidpid_bootrom_ok && *chip == rp2040)) { + if (!usb_ser_match) { char ser_str[128]; libusb_get_string_descriptor_ascii(*dev_handle, desc.iSerialNumber, (unsigned char*)ser_str, sizeof(ser_str)); if (strcmp(ser, ser_str)) { return dr_vidpid_unknown; + } else { + usb_ser_match = true; } } } - // Runtime reset interface detection - if (!ret) { + // Runtime reset interface detection - only runs if USB serial number above matched, or no serial number passed + if (!ret && usb_ser_match) { for (int i = 0; i < config->bNumInterfaces; i++) { if (config->interface[i].altsetting[0].bInterfaceClass == 0xff && config->interface[i].altsetting[0].bInterfaceSubClass == RESET_INTERFACE_SUBCLASS && diff --git a/picoboot_connection/picoboot_connection.h b/picoboot_connection/picoboot_connection.h index 5e2f920e..7e172fe6 100644 --- a/picoboot_connection/picoboot_connection.h +++ b/picoboot_connection/picoboot_connection.h @@ -49,8 +49,10 @@ enum picoboot_device_result { dr_vidpid_stdio_usb_cant_connect, // stdio_usb vid/pid dr_vidpid_cant_connect, // custom vid/pid - // Other known pids for no device found messages + // Special known pids that require extra detection (e.g. USB reset interface) dr_vidpid_debugprobe, // debugprobe vid/pid, with a USB reset interface - only returned when not searching for debugprobe vid/pid + + // Other known pids for no device found messages dr_vidpid_micropython, // micropython vid/pid dr_vidpid_circuitpython, // circuitpython vid/pid dr_vidpid_debugprobe_old, // old debugprobe vid/pid (v1.0 and v1.0.1) From dd9709dea43448a52b6fcd8882ffc4f8219a7a54 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 24 Jul 2026 14:42:23 +0100 Subject: [PATCH 7/7] Fix --debugprobe on Windows Apply the force_rp2040 workaround based on chip, not the PID --- main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 88798316..261c8e9f 100644 --- a/main.cpp +++ b/main.cpp @@ -10452,6 +10452,7 @@ int main(int argc, char **argv) { if (selected_cmd->force_requires_pre_reboot()) { if (!tries) { // we reboot into BOOTSEL mode and disable MSC interface (the 1 here) + auto to_reboot_chip = std::get<0>(devices[dr_vidpid_usb_reset][0]); auto &to_reboot = std::get<1>(devices[dr_vidpid_usb_reset][0]); auto &to_reboot_handle = std::get<2>(devices[dr_vidpid_usb_reset][0]); unsigned int disable_mask = 1; // disable MSC interface @@ -10459,7 +10460,7 @@ int main(int argc, char **argv) { { struct libusb_device_descriptor desc; libusb_get_device_descriptor(to_reboot, &desc); - if (desc.idProduct == PRODUCT_ID_RP2040_STDIO_USB || settings.force_rp2040) { + if (to_reboot_chip == rp2040 || settings.force_rp2040) { // the Zadig driver should be setup for the device in BOOTSEL mode with no interfaces disabled, // as all the interfaces are enabled when you plug it in while holding down the BOOTSEL button disable_mask = 0;