Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add VID and PID device target filters #85

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ PICOTOOL:
Tool for interacting with a RP2040 device in BOOTSEL mode, or with a RP2040 binary

SYNOPSIS:
picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [-f] [-F]
picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F]
picotool info [-b] [-p] [-d] [-l] [-a] <filename> [-t <type>]
picotool load [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [-f] [-F]
picotool save [-p] [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
picotool save -a [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
picotool verify [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>] [-r <from> <to>] [-o <offset>]
picotool reboot [-a] [-u] [--bus <bus>] [--address <addr>] [-f] [-F]
picotool load [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F]
picotool save [-p] [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool save -a [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool verify [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F] <filename> [-t <type>] [-r <from> <to>] [-o <offset>]
picotool reboot [-a] [-u] [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F]
picotool version [-s]
picotool help [<cmd>]

Expand Down Expand Up @@ -111,7 +111,7 @@ INFO:
Without any arguments, this will display basic information for all connected RP2040 devices in BOOTSEL mode

SYNOPSIS:
picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [-f] [-F]
picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F]
picotool info [-b] [-p] [-d] [-l] [-a] <filename> [-t <type>]

OPTIONS:
Expand All @@ -133,6 +133,12 @@ TARGET SELECTION:
Filter devices by USB bus number
--address <addr>
Filter devices by USB device address
--vid <vid>
Filter devices by vendor id
--pid <pid>
Filter devices by product id
--serial <serial>
Filter devices by serial id
-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
Expand Down Expand Up @@ -218,7 +224,7 @@ LOAD:
Load the program / memory range stored in a file onto the device.

SYNOPSIS:
picotool load [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [-f] [-F]
picotool load [-n] [-N] [-u] [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F]

OPTIONS:
Post load actions
Expand Down Expand Up @@ -249,6 +255,12 @@ OPTIONS:
Filter devices by USB bus number
--address <addr>
Filter devices by USB device address
--vid <vid>
Filter devices by vendor id
--pid <pid>
Filter devices by product id
--serial <serial>
Filter devices by serial id
-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
Expand All @@ -275,9 +287,9 @@ SAVE:
Save the program / memory stored in flash on the device to a file.

SYNOPSIS:
picotool save [-p] [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
picotool save -a [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
picotool save [-p] [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool save -a [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [--vid <vid>] [--pid <pid>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]

OPTIONS:
Selection of data to save
Expand All @@ -297,6 +309,12 @@ OPTIONS:
Filter devices by USB bus number
--address <addr>
Filter devices by USB device address
--vid <vid>
Filter devices by vendor id
--pid <pid>
Filter devices by product id
--serial <serial>
Filter devices by serial id
-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
Expand Down
138 changes: 112 additions & 26 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <memory>
#include <functional>
#include <ctime>
#include <sstream>

#include "boot/uf2.h"
#include "picoboot_connection_cxx.h"
Expand Down Expand Up @@ -255,6 +256,10 @@ struct _settings {
uint32_t binary_start = FLASH_START;
int bus=-1;
int address=-1;
int port = -1;
int vid = -1;
int pid = -1;
string serial;
uint32_t offset = 0;
uint32_t from = 0;
uint32_t to = 0;
Expand Down Expand Up @@ -295,12 +300,31 @@ std::shared_ptr<cmd> selected_cmd;
auto device_selection =
(
(option("--bus") & integer("bus").min_value(0).max_value(255).set(settings.bus)
.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"
.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"
).min(0).doc_non_optional(true) +
(
(option("--vid") & hex("vid").min_value(0).max_value(0xffff).set(settings.vid)
.if_missing([] { return "missing vendor id"; }))
% "Filter devices by vendor id"
+ (option("--pid") & hex("pid").min_value(0).max_value(0xffff).set(settings.pid)
.if_missing([] { return "missing product id"; }))
% "Filter devices by product id"
+ (option("--serial") & value("serial").set(settings.serial)
.if_missing([] { return "missing serial id"; }))
% "Filter devices by serial id"
#if !defined(_WIN32)
+ 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 RPI-RP2 drive mounted"
+ 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 RPI-RP2 drive mounted"
#endif
).min(0).doc_non_optional(true);

Expand Down Expand Up @@ -1582,28 +1606,34 @@ void info_guts(memory_access &raw_access) {
}

string missing_device_string(bool wasRetry) {
char b[256];
if (wasRetry) {
strcpy(b, "Despite the reboot attempt, no ");
} else {
strcpy(b, "No ");
std::ostringstream oss;
oss << (wasRetry ? "Despite the reboot attempt, no" : "No") << " accessible RP2040 device";
if (settings.bus != -1 && settings.serial.empty()) {
oss << 's';
}
oss << " in BOOTSEL mode";

if (settings.vid != -1 && settings.pid != -1) {
oss << " with ID '" << std::hex << settings.vid << ':' << std::hex << settings.pid << '\'';
} else if (settings.vid != -1) {
oss << " with Vendor ID '" << std::hex << settings.vid << '\'';
} else if (settings.pid != -1) {
oss << " with Product ID '" << std::hex << settings.pid << '\'';
}

if (!settings.serial.empty()) {
oss << " with serial ID '" << settings.serial << '\'';
}
oss << " was found";
if (settings.bus != -1) {
oss << " at bus " << settings.bus;
}
char *buf = b + strlen(b);
int buf_len = b + sizeof(b) - buf;
if (settings.address != -1) {
if (settings.bus != -1) {
snprintf(buf, buf_len, "accessible RP2040 device in BOOTSEL mode was found at bus %d, address %d.", settings.bus, settings.address);
} else {
snprintf(buf, buf_len, "accessible RP2040 devices in BOOTSEL mode were found with address %d.", settings.address);
}
} else {
if (settings.bus != -1) {
snprintf(buf, buf_len, "accessible RP2040 devices in BOOTSEL mode were found found on bus %d.", settings.bus);
} else {
snprintf(buf, buf_len,"accessible RP2040 devices in BOOTSEL mode were found.");
}
oss << (settings.bus == -1 ? " with" : ",") << " address " << settings.address;
}
return b;
oss << '.';

return oss.str();
}

bool help_command::execute(device_map &devices) {
Expand Down Expand Up @@ -2180,6 +2210,46 @@ void cancelled(int) {
throw cancelled_exception();
}

string get_usb_device_serial(libusb_device *device, libusb_device_handle *handle) {
struct libusb_device_descriptor desc{};
int ret = libusb_get_device_descriptor(device, &desc);
if (ret) {
return "";
}
bool needs_closing = false;
if (handle == nullptr) {
ret = libusb_open(device, &handle);
if (ret) {
return "";
}
needs_closing = true;
}
unsigned char data[33];
string serial;
if (libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, data, 31)) {
data[32] = '\0';
serial = reinterpret_cast<char const*>(data);
}
if (needs_closing) {
libusb_close(handle);
}

return serial;
}

int get_usb_device_vid_pid(libusb_device *device, uint16_t &vid_out, uint16_t &pid_out) {
struct libusb_device_descriptor desc;
int ret = libusb_get_device_descriptor(device, &desc);
if (ret) {
return -1;
}

vid_out = desc.idVendor;
pid_out = desc.idProduct;

return 0;
}

int main(int argc, char **argv) {
libusb_context *ctx = nullptr;

Expand Down Expand Up @@ -2225,11 +2295,19 @@ int main(int argc, char **argv) {
for (libusb_device **dev = devs; *dev; dev++) {
if (settings.bus != -1 && settings.bus != libusb_get_bus_number(*dev)) continue;
if (settings.address != -1 && settings.address != libusb_get_device_address(*dev)) continue;
if (settings.port != -1 && settings.port != libusb_get_port_number(*dev)) continue;
if (settings.vid != -1 || settings.pid != -1) {
uint16_t vid, pid;
int ret = get_usb_device_vid_pid(*dev, vid, pid);
if (settings.vid != -1 && (ret || settings.vid != vid)) continue;
if (settings.pid != -1 && (ret || settings.pid != vid)) continue;
}
libusb_device_handle *handle = nullptr;
auto result = picoboot_open_device(*dev, &handle);
if (handle) {
to_close.push_back(handle);
}
if (!settings.serial.empty() && settings.serial != get_usb_device_serial(*dev, handle)) continue;
if (result != dr_error) {
devices[result].push_back(std::make_pair(*dev, handle));
}
Expand Down Expand Up @@ -2311,8 +2389,14 @@ int main(int argc, char **argv) {
"Forced command requires a single rebootable RP2040 device to be targeted.");
}
if (selected_cmd->force_requires_pre_reboot()) {
// we reboot into BOOTSEL mode and disable MSC interface (the 1 here)
auto &to_reboot = devices[dr_vidpid_stdio_usb][0].first;
// adjust filter settings, so we can find the device after reboot
if (!settings.serial.empty()) {
settings.serial = "";
settings.bus = libusb_get_bus_number(to_reboot);
settings.port = libusb_get_port_number(to_reboot);
}
// we reboot into BOOTSEL mode and disable MSC interface (the 1 here)
reboot_device(to_reboot, true, 1);
fos << "The device was asked to reboot into BOOTSEL mode so the command can be executed.\n\n";
for (const auto &handle : to_close) {
Expand All @@ -2329,7 +2413,9 @@ int main(int argc, char **argv) {
// again is to assume it is the only now visible device.
settings.force = false;
settings.address = -1;
settings.bus = -1;
if (settings.serial.empty()) {
settings.bus = -1;
}
continue;
}
}
Expand Down