esp_usb_jtag: disambiguate multiple boards via --busdev-num and --usb-serial-num
libusb_open_device_with_vid_pid() returns the first VID:PID match, so with two ESP32-S3 cables connected, every invocation programmed the same board. Replace it with a libusb_get_device_list() iteration that also honours cable.bus_addr/device_addr (already exposed as --busdev-num) and --usb-serial-num flag that matches the device iSerialNumber by substring (useful with MAC-derived serials). The -d /dev/ttyACM* path could not help: that's the CDC-ACM interface (iface 0), while JTAG is on the vendor iface 2 reached via libusb; ttyACM numbering and libusb enumeration order are independent. Constraint: must keep existing single-board invocations working without flag changes Confidence: high Scope-risk: narrow Directive: arguments struct uses positional aggregate init at main.cpp:120; new fields must add a matching slot Not-tested: simultaneous two-board programming on real hardware (single-board path verified to build) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4000496abc
commit
9e548983f9
|
|
@ -70,7 +70,9 @@ openFPGALoader -- a program to flash FPGA
|
|||
--cable-index arg probe index (FTDI and cmsisDAP)
|
||||
--busdev-num arg select a probe by it bus and device number
|
||||
(bus_num:device_addr)
|
||||
--ftdi-serial arg FTDI chip serial number
|
||||
--usb-serial-num arg USB iSerial (FTDI chip serial number or ESP32
|
||||
iSerialNumber substring)
|
||||
--ftdi-serial arg FTDI chip serial number (Deprecated)
|
||||
--ftdi-channel arg FTDI chip channel number (channels 0-3 map to
|
||||
A-D)
|
||||
-d, --device arg device to use (/dev/ttyUSBx)
|
||||
|
|
|
|||
|
|
@ -245,13 +245,17 @@ static uint16_t esp_usb_target_chip_id = 0; /* not applicable for FPGA, they hav
|
|||
|
||||
/* end copy from openocd */
|
||||
|
||||
esp_usb_jtag::esp_usb_jtag(uint32_t clkHZ, int8_t verbose, int vid = ESPUSBJTAG_VID, int pid = ESPUSBJTAG_PID):
|
||||
esp_usb_jtag::esp_usb_jtag(uint32_t clkHZ, int8_t verbose,
|
||||
int vid = ESPUSBJTAG_VID, int pid = ESPUSBJTAG_PID,
|
||||
uint8_t bus_addr = 0, uint8_t dev_addr = 0,
|
||||
const std::string &serial = ""):
|
||||
_verbose(verbose > 1),
|
||||
dev_handle(NULL), usb_ctx(NULL), _tdi(0), _tms(0),
|
||||
/* Default for emard firmware. */
|
||||
_esp_usb_jtag_caps(0x2000), _write_ep(0x02),
|
||||
_vid(ESPUSBJTAG_VID), _pid(ESPUSBJTAG_PID)
|
||||
{
|
||||
libusb_device **devs = NULL;
|
||||
int ret;
|
||||
char mess[256];
|
||||
|
||||
|
|
@ -260,18 +264,61 @@ esp_usb_jtag::esp_usb_jtag(uint32_t clkHZ, int8_t verbose, int vid = ESPUSBJTAG_
|
|||
throw std::exception();
|
||||
}
|
||||
|
||||
dev_handle = libusb_open_device_with_vid_pid(usb_ctx,
|
||||
_vid, _pid);
|
||||
if (!dev_handle) {
|
||||
_esp_usb_jtag_caps = 0x030A;
|
||||
_write_ep = 0x03;
|
||||
_pid = 0x1002;
|
||||
dev_handle = libusb_open_device_with_vid_pid(usb_ctx,
|
||||
_vid, _pid);
|
||||
ssize_t cnt = libusb_get_device_list(usb_ctx, &devs);
|
||||
if (cnt < 0) {
|
||||
std::cerr << "esp_usb_jtag: libusb_get_device_list failed: "
|
||||
<< libusb_error_name(static_cast<int>(cnt)) << std::endl;
|
||||
libusb_exit(usb_ctx);
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
for (ssize_t i = 0; i < cnt; i++) {
|
||||
libusb_device *dev = devs[i];
|
||||
struct libusb_device_descriptor desc;
|
||||
if (libusb_get_device_descriptor(dev, &desc) < 0)
|
||||
continue;
|
||||
if (desc.idVendor != vid || desc.idProduct != pid)
|
||||
continue;
|
||||
/* bus/device filter (only when both are user-supplied) */
|
||||
if (bus_addr != 0 && dev_addr != 0 &&
|
||||
(libusb_get_bus_number(dev) != bus_addr ||
|
||||
libusb_get_device_address(dev) != dev_addr))
|
||||
continue;
|
||||
|
||||
/* serial filter */
|
||||
if (!serial.empty()) {
|
||||
libusb_device_handle *probe = NULL;
|
||||
if (libusb_open(dev, &probe) < 0)
|
||||
continue;
|
||||
unsigned char raw[256] = {0};
|
||||
int n = 0;
|
||||
if (desc.iSerialNumber)
|
||||
n = libusb_get_string_descriptor_ascii(probe,
|
||||
desc.iSerialNumber, raw, sizeof(raw));
|
||||
libusb_close(probe);
|
||||
if (n <= 0)
|
||||
continue;
|
||||
const std::string found(reinterpret_cast<char *>(raw), n);
|
||||
|
||||
if (found.find(serial) == std::string::npos)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (libusb_open(dev, &dev_handle) == 0)
|
||||
break;
|
||||
dev_handle = NULL;
|
||||
}
|
||||
libusb_free_device_list(devs, 1);
|
||||
|
||||
if (!dev_handle) {
|
||||
snprintf(mess, 256, "fails to open esp_usb_jtag device");
|
||||
printError(mess);
|
||||
std::cerr << "fails to open esp_usb_jtag device vid:pid 0x"
|
||||
<< std::hex << vid << ":0x" << pid;
|
||||
if (bus_addr || dev_addr)
|
||||
std::cerr << " bus:dev " << std::dec << static_cast<int>(bus_addr)
|
||||
<< ":" << static_cast<int>(dev_addr);
|
||||
if (!serial.empty())
|
||||
std::cerr << " serial '" << serial << "'";
|
||||
std::cerr << std::endl;
|
||||
libusb_exit(usb_ctx);
|
||||
throw std::exception();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include <libusb.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "jtagInterface.hpp"
|
||||
|
||||
/*!
|
||||
|
|
@ -23,7 +25,9 @@
|
|||
|
||||
class esp_usb_jtag : public JtagInterface {
|
||||
public:
|
||||
esp_usb_jtag(uint32_t clkHZ, int8_t verbose, int vid, int pid);
|
||||
esp_usb_jtag(uint32_t clkHZ, int8_t verbose, int vid, int pid,
|
||||
uint8_t bus_addr, uint8_t dev_addr,
|
||||
const std::string &serial);
|
||||
virtual ~esp_usb_jtag();
|
||||
|
||||
int setClkFreq(uint32_t clkHZ) override;
|
||||
|
|
|
|||
|
|
@ -165,7 +165,8 @@ Jtag::Jtag(const cable_t &cable, const jtag_pins_conf_t *pin_conf,
|
|||
break;
|
||||
case MODE_ESP:
|
||||
#ifdef ENABLE_ESP_USB
|
||||
_jtag = new esp_usb_jtag(clkHZ, verbose, 0x303a, 0x1001);
|
||||
_jtag = new esp_usb_jtag(clkHZ, verbose, 0x303a, 0x1001,
|
||||
cable.bus_addr, cable.device_addr, serial);
|
||||
#else
|
||||
std::cerr << "Jtag: support for esp32s3 cable was not enabled at compile time" << std::endl;
|
||||
throw std::exception();
|
||||
|
|
|
|||
14
src/main.cpp
14
src/main.cpp
|
|
@ -272,6 +272,14 @@ int main(int argc, char **argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (!args.usb_serial_num.empty()) {
|
||||
if (cable.type != MODE_FTDI_SERIAL && cable.type != MODE_FTDI_BITBANG &&
|
||||
cable.type != MODE_ESP){
|
||||
printError("Error: usb-serial-num param is for FTDI and esp32s3 cables.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.vid != 0) {
|
||||
printInfo("Cable VID overridden");
|
||||
cable.vid = args.vid;
|
||||
|
|
@ -921,7 +929,7 @@ int parse_opt(int argc, char **argv, struct arguments *args,
|
|||
("busdev-num",
|
||||
"select a probe by it bus and device number (bus_num:device_addr)",
|
||||
cxxopts::value<std::vector<std::string>>(bus_dev_num))
|
||||
("usb-serial-num", "USB iSerial (FTDI chip serial number)",
|
||||
("usb-serial-num", "USB iSerial (FTDI chip serial number or ESP32 iSerialNumber substring)",
|
||||
cxxopts::value<std::string>(args->usb_serial_num))
|
||||
("ftdi-serial", "FTDI chip serial number (Deprecated)",
|
||||
cxxopts::value<std::string>(ftdi_serial))
|
||||
|
|
@ -1131,8 +1139,8 @@ int parse_opt(int argc, char **argv, struct arguments *args,
|
|||
}
|
||||
|
||||
if (result.count("ftdi-serial")) {
|
||||
if (result.count("usb_serial_num")) {
|
||||
printError("Error: ftdi_serial and usb_serial_num can't be used at the same time.");
|
||||
if (result.count("usb-serial-num")) {
|
||||
printError("Error: ftdi-serial and usb-serial-num can't be used at the same time.");
|
||||
return -1;
|
||||
}
|
||||
args->usb_serial_num = ftdi_serial;
|
||||
|
|
|
|||
Loading…
Reference in New Issue