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 Serial ID device target filter #84

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
28 changes: 17 additions & 11 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>] [--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>] [--serial <serial>] [-f] [-F]
picotool save [-p] [--bus <bus>] [--address <addr>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool save -a [--bus <bus>] [--address <addr>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool verify [--bus <bus>] [--address <addr>] [--serial <serial>] [-f] [-F] <filename> [-t <type>] [-r <from> <to>] [-o <offset>]
picotool reboot [-a] [-u] [--bus <bus>] [--address <addr>] [--serial <serial>] [-f] [-F]
picotool version [-s]
picotool help [<cmd>]

Expand Down Expand Up @@ -133,6 +133,8 @@ TARGET SELECTION:
Filter devices by USB bus number
--address <addr>
Filter devices by USB device address
--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 +220,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>] [--serial <serial>] [-f] [-F]

OPTIONS:
Post load actions
Expand Down Expand Up @@ -249,6 +251,8 @@ OPTIONS:
Filter devices by USB bus number
--address <addr>
Filter devices by USB device address
--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 +279,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>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool save -a [--bus <bus>] [--address <addr>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]
picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [--serial <serial>] [-f] [-F] <filename> [-t <type>]

OPTIONS:
Selection of data to save
Expand All @@ -297,6 +301,8 @@ OPTIONS:
Filter devices by USB bus number
--address <addr>
Filter devices by USB device address
--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
102 changes: 76 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,8 @@ struct _settings {
uint32_t binary_start = FLASH_START;
int bus=-1;
int address=-1;
int port = -1;
string serial;
uint32_t offset = 0;
uint32_t from = 0;
uint32_t to = 0;
Expand Down Expand Up @@ -295,12 +298,25 @@ 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("--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 +1598,25 @@ 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.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 +2193,33 @@ 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 main(int argc, char **argv) {
libusb_context *ctx = nullptr;

Expand Down Expand Up @@ -2225,11 +2265,13 @@ 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;
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 +2353,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 +2377,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