diff --git a/README.md b/README.md index 6a73de81..af59fb7d 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,8 @@ TARGET SELECTION: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -221,6 +223,8 @@ TARGET SELECTION: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -322,6 +326,8 @@ OPTIONS: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -388,6 +394,8 @@ OPTIONS: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -462,6 +470,8 @@ OPTIONS: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -511,6 +521,8 @@ OPTIONS: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -724,6 +736,8 @@ OPTIONS: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -878,6 +892,8 @@ OPTIONS: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -977,6 +993,8 @@ TARGET SELECTION: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -1037,6 +1055,8 @@ TARGET SELECTION: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -1084,6 +1104,8 @@ OPTIONS: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -1128,6 +1150,8 @@ OPTIONS: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -1213,6 +1237,8 @@ OPTIONS: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode @@ -1278,6 +1304,8 @@ TARGET SELECTION: Filter by product id --ser Filter by serial number + --rp2040 + Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms) -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 a 'reboot') the device will be rebooted back to application mode diff --git a/main.cpp b/main.cpp index 0aef1b97..f81ba6c5 100644 --- a/main.cpp +++ b/main.cpp @@ -465,6 +465,7 @@ struct _settings { int vid=-1; int pid=-1; string ser; + bool force_rp2040 = false; uint32_t offset = 0; uint32_t from = 0; uint32_t to = 0; @@ -593,6 +594,7 @@ auto device_selection = (option("--vid") & integer("vid").set(settings.vid).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) % "Use RP2040 workarounds on Windows when using custom vid/pid (ignored on other platforms)" + 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" ).min(0).doc_non_optional(true).collapse_synopsys("device-selection"); @@ -8624,6 +8626,13 @@ int main(int argc, char **argv) { if (result != dr_error) { devices[result].emplace_back(std::make_tuple(model, *dev, handle)); } + + if (settings.vid == 0 && !settings.ser.empty() && !devices[dr_vidpid_bootrom_ok].empty()) { + // Searching with no vid/pid filtering (ie opening all devices) can cause issues, so stop + // searching when we have a serial number, as we know we have the correct device + DEBUG_LOG("Found bootrom device with serial number, so stopping search"); + break; + } } } auto supported = selected_cmd->get_device_support(); @@ -8641,6 +8650,11 @@ int main(int argc, char **argv) { bool had_note = false; fos << missing_device_string(tries>0, selected_cmd->requires_rp2350()); if (tries) { +#if defined(_WIN32) + if (settings.force_rp2040) { + fos << " You may need to install a driver via Zadig. See \"Getting started with Raspberry Pi Pico\" for more information."; + } +#endif fos << " It is possible the device is not responding, and will have to be manually entered into BOOTSEL mode.\n"; had_note = true; // suppress "but:" in this case } @@ -8672,13 +8686,8 @@ int main(int argc, char **argv) { printer(dr_vidpid_micropython, " appears to be an RP-series MicroPython device not in BOOTSEL mode."); if (selected_cmd->force_requires_pre_reboot()) { - #if defined(_WIN32) - printer(dr_vidpid_stdio_usb, - " appears to have a USB serial connection, not in BOOTSEL mode. You can force reboot into BOOTSEL mode via 'picotool reboot -f -u' first."); - #else 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."); - #endif } else { // special case message for what is actually just reboot (the only command that doesn't require reboot first) printer(dr_vidpid_stdio_usb, @@ -8718,13 +8727,14 @@ int main(int argc, char **argv) { // 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]); + unsigned int disable_mask = 1; // disable MSC interface #if defined(_WIN32) { struct libusb_device_descriptor desc; libusb_get_device_descriptor(to_reboot, &desc); - if (desc.idProduct == PRODUCT_ID_RP2040_STDIO_USB) { - fail(ERROR_NOT_POSSIBLE, - "Forced commands do not work with RP2040 on Windows - you can force reboot into BOOTSEL mode via 'picotool reboot -f -u' instead."); + if (desc.idProduct == PRODUCT_ID_RP2040_STDIO_USB || settings.force_rp2040) { + disable_mask = 0; // enable MSC interface so Zadig works correctly + settings.force_rp2040 = true; } } #endif @@ -8741,7 +8751,7 @@ int main(int argc, char **argv) { } } - reboot_device(to_reboot, to_reboot_handle, true, 1); + reboot_device(to_reboot, to_reboot_handle, true, disable_mask); fos << "The device was asked to reboot into BOOTSEL mode so the command can be executed."; } else if (tries == 1) { fos << "\nWaiting for device to reboot"; @@ -8762,6 +8772,13 @@ int main(int argc, char **argv) { // again is to assume it has the same serial number. settings.address = -1; settings.bus = -1; + if (settings.pid != -1 || settings.vid != -1) { + // also skip vid/pid filtering, as that should change in BOOTSEL mode, and could be white-labelled on RP2350 + settings.pid = -1; + // still filter for rpi vid/pid if we don't have a serial number, as that is an RP2040 running a no_flash binary, so will + // have a standard rpi vid/pid in BOOTSEL mode + settings.vid = settings.ser.empty() ? -1 : 0; // 0 means skip vid/pid filtering entirely, -1 means filter for rpi vid/pid + } continue; } } diff --git a/picoboot_connection/picoboot_connection.c b/picoboot_connection/picoboot_connection.c index 265608c7..0dc3967c 100644 --- a/picoboot_connection/picoboot_connection.c +++ b/picoboot_connection/picoboot_connection.c @@ -69,7 +69,7 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d int ret = libusb_get_device_descriptor(device, &desc); enum picoboot_device_result res = dr_vidpid_unknown; if (ret && verbose) { - output("Failed to read device descriptor\n"); + output("Failed to read device descriptor %s\n", libusb_error_name(ret)); } if (!ret) { if (pid >= 0) { @@ -107,14 +107,14 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d } ret = libusb_get_active_config_descriptor(device, &config); if (ret && verbose) { - output("Failed to read config descriptor\n"); + output("Failed to read config descriptor %s\n", libusb_error_name(ret)); } } if (!ret) { ret = libusb_open(device, dev_handle); if (ret && verbose) { - output("Failed to open device %d\n", ret); + output("Failed to open device %s\n", libusb_error_name(ret)); } if (ret) { if (vid == 0 || strlen(ser) != 0) { @@ -169,7 +169,7 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d if (verbose) output("Found PICOBOOT interface\n"); ret = libusb_claim_interface(*dev_handle, interface); if (ret) { - if (verbose) output("Failed to claim interface\n"); + if (verbose) output("Failed to claim interface %s\n", libusb_error_name(ret)); return dr_vidpid_bootrom_no_interface; } } else { @@ -180,16 +180,22 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d if (!ret) { if (*model == unknown) { - struct picoboot_get_info_cmd info_cmd; - info_cmd.bType = PICOBOOT_GET_INFO_SYS, - info_cmd.dParams[0] = (uint32_t) (SYS_INFO_CHIP_INFO); - uint32_t word_buf[64]; - // RP2040 doesn't have this function, so returns non-zero - int info_ret = picoboot_get_info(*dev_handle, &info_cmd, (uint8_t*)word_buf, sizeof(word_buf)); - if (info_ret) { + if (desc.idVendor == VENDOR_ID_RASPBERRY_PI && desc.idProduct == PRODUCT_ID_RP2040_USBBOOT) { + // Set model based on bootrom vid/pid for RP2040, as it cannot be white-labelled *model = rp2040; } else { - *model = rp2350; + // Otherwise check the chip info command exists + struct picoboot_get_info_cmd info_cmd; + info_cmd.bType = PICOBOOT_GET_INFO_SYS, + info_cmd.dParams[0] = (uint32_t) (SYS_INFO_CHIP_INFO); + uint32_t word_buf[64]; + // Other devices don't have this function, so will return errors + int info_ret = picoboot_get_info(*dev_handle, &info_cmd, (uint8_t*)word_buf, sizeof(word_buf)); + if (info_ret) { + return dr_vidpid_unknown; + } else { + *model = rp2350; + } } } if (strlen(ser) != 0) { @@ -301,7 +307,7 @@ int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uin ret = libusb_bulk_transfer(usb_device, out_ep, (uint8_t *) cmd, sizeof(struct picoboot_cmd), &sent, 3000); if (ret != 0 || sent != sizeof(struct picoboot_cmd)) { - output(" ...failed to send command %d\n", ret); + output(" ...failed to send command %s\n", libusb_error_name(ret)); return ret; } @@ -321,7 +327,7 @@ int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uin int received = 0; ret = libusb_bulk_transfer(usb_device, in_ep, buffer, cmd->dTransferLength, &received, timeout); if (ret != 0 || received != (int) cmd->dTransferLength) { - output(" ...failed to receive data %d %d/%d\n", ret, received, cmd->dTransferLength); + output(" ...failed to receive data %s %d/%d\n", libusb_error_name(ret), received, cmd->dTransferLength); if (!ret) ret = 1; return ret; } @@ -329,7 +335,7 @@ int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uin if (verbose) output(" send %d...\n", cmd->dTransferLength); ret = libusb_bulk_transfer(usb_device, out_ep, buffer, cmd->dTransferLength, &sent, timeout); if (ret != 0 || sent != (int) cmd->dTransferLength) { - output(" ...failed to send data %d %d/%d\n", ret, sent, cmd->dTransferLength); + output(" ...failed to send data %s %d/%d\n", libusb_error_name(ret), sent, cmd->dTransferLength); if (!ret) ret = 1; picoboot_cmd_status_verbose(usb_device, NULL, true); return ret;