From 447b176d11881bc3daeef5505dea60493b560a9f Mon Sep 17 00:00:00 2001 From: MikeRaffa Date: Mon, 4 Dec 2023 01:28:53 -0500 Subject: [PATCH 1/4] Issue #973: Differentiate multiple USB programmers of the same VID/PID (libusb or hidapi or libftdi) Added code for USBasp to check for bus:device match using the -P option. Syntax is -P usb:: (same as current USBTiny implementation). This also supports serial number check with the alternative -P usb: format (no ':'). In verbose mode, prints out bus/device/serial number for any found USBasps. Only tested on Linux, but it works with HAVE_LIBUSB_1_0 on or off (there's some slightly different code in the two versions of usbOpenDevice()). --- src/usbasp.c | 72 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/src/usbasp.c b/src/usbasp.c index d9bca89e..4971a23e 100644 --- a/src/usbasp.c +++ b/src/usbasp.c @@ -162,9 +162,9 @@ static int usbasp_transmit(const PROGRAMMER *pgm, unsigned char receive, unsigned char functionid, const unsigned char *send, unsigned char *buffer, int buffersize); #ifdef USE_LIBUSB_1_0 -static int usbOpenDevice(libusb_device_handle **device, int vendor, const char *vendorName, int product, const char *productName); +static int usbOpenDevice(libusb_device_handle **device, int vendor, const char *vendorName, int product, const char *productName, const char *port); #else -static int usbOpenDevice(usb_dev_handle **device, int vendor, const char *vendorName, int product, const char *productName); +static int usbOpenDevice(usb_dev_handle **device, int vendor, const char *vendorName, int product, const char *productName, const char *port); #endif // interface - prog. static int usbasp_open(PROGRAMMER *pgm, const char *port); @@ -399,6 +399,37 @@ static int usbasp_transmit(const PROGRAMMER *pgm, return nbytes; } +static int check_for_port_argument_match(const char *port, char *bus, char *device, char *serial_num) { + + if (verbose) { + pmsg_notice("usbOpenDevice(): found USBasp, bus:device: %s:%s, serial_number: %s\n", bus, device, serial_num); + } + const size_t usb_len = strlen("usb"); + if(str_starts(port, "usb") && ':' == port[usb_len]) { + port += usb_len + 1; + char *colon_pointer = strchr(port, ':'); + if (colon_pointer) { + // Value contains ':' character. Compare with bus/device. + if (strncmp(port, bus, colon_pointer - port)) { + return 1; + } + port = colon_pointer + 1; + if (strcmp(port, device)) { + return 1; + } + return 0; + } else { + // serial number case + if (!str_eq(serial_num, port)) { + return 1; + } else { + return 0; + } + } + } + // Invalid -P option. + return 1; +} /* * Try to open USB device with given VID, PID, vendor and product name @@ -407,8 +438,8 @@ static int usbasp_transmit(const PROGRAMMER *pgm, * shared VID/PID */ #ifdef USE_LIBUSB_1_0 -static int usbOpenDevice(libusb_device_handle **device, int vendor, - const char *vendorName, int product, const char *productName) +static int usbOpenDevice(libusb_device_handle **device, int vendor, const char *vendorName, + int product, const char *productName, const char *port) { libusb_device_handle *handle = NULL; int errorCode = USB_ERROR_NOTFOUND; @@ -463,6 +494,19 @@ static int usbOpenDevice(libusb_device_handle **device, int vendor, if((productName != NULL) && (productName[0] != 0) && !str_eq(string, productName)) errorCode = USB_ERROR_NOTFOUND; } + if (errorCode == 0) { + if(!str_eq(port, "usb")) { + // -P option given + libusb_get_string_descriptor_ascii(handle, descriptor.iSerialNumber, (unsigned char*)string, sizeof(string)); + char bus_num[4]; + sprintf(bus_num, "%d", libusb_get_bus_number(dev)); + char dev_addr[4]; + sprintf(dev_addr, "%d", libusb_get_device_address(dev)); + if (check_for_port_argument_match(port, bus_num, dev_addr, string)) { + errorCode = USB_ERROR_NOTFOUND; + } + } + } if (errorCode == 0) break; libusb_close(handle); @@ -477,8 +521,8 @@ static int usbOpenDevice(libusb_device_handle **device, int vendor, return errorCode; } #else -static int usbOpenDevice(usb_dev_handle **device, int vendor, - const char *vendorName, int product, const char *productName) +static int usbOpenDevice(usb_dev_handle **device, int vendor, const char *vendorName, + int product, const char *productName, const char *port) { struct usb_bus *bus; struct usb_device *dev; @@ -533,6 +577,16 @@ static int didUsbInit = 0; if((productName != NULL) && (productName[0] != 0) && !str_eq(string, productName)) errorCode = USB_ERROR_NOTFOUND; } + if (errorCode == 0) { + if(!str_eq(port, "usb")) { + // -P option given + usb_get_string_simple(handle, dev->descriptor.iSerialNumber, + string, sizeof(string)); + if (check_for_port_argument_match(port, bus->dirname, dev->filename, string)) { + errorCode = USB_ERROR_NOTFOUND; + } + } + } if (errorCode == 0) break; usb_close(handle); @@ -566,14 +620,14 @@ static int usbasp_open(PROGRAMMER *pgm, const char *port) { pid = USBASP_SHARED_PID; } vid = pgm->usbvid? pgm->usbvid: USBASP_SHARED_VID; - if (usbOpenDevice(&PDATA(pgm)->usbhandle, vid, pgm->usbvendor, pid, pgm->usbproduct) != 0) { + if (usbOpenDevice(&PDATA(pgm)->usbhandle, vid, pgm->usbvendor, pid, pgm->usbproduct, port) != 0) { /* try alternatives */ if(str_eq(pgmid, "usbasp")) { /* for id usbasp autodetect some variants */ if(str_caseeq(port, "nibobee")) { pmsg_error("using -C usbasp -P nibobee is deprecated, use -C nibobee instead\n"); if (usbOpenDevice(&PDATA(pgm)->usbhandle, USBASP_NIBOBEE_VID, "www.nicai-systems.com", - USBASP_NIBOBEE_PID, "NIBObee") != 0) { + USBASP_NIBOBEE_PID, "NIBObee", port) != 0) { pmsg_error("cannot find USB device NIBObee with vid=0x%x pid=0x%x\n", USBASP_NIBOBEE_VID, USBASP_NIBOBEE_PID); return -1; @@ -582,7 +636,7 @@ static int usbasp_open(PROGRAMMER *pgm, const char *port) { } /* check if device with old VID/PID is available */ if (usbOpenDevice(&PDATA(pgm)->usbhandle, USBASP_OLD_VID, "www.fischl.de", - USBASP_OLD_PID, "USBasp") == 0) { + USBASP_OLD_PID, "USBasp", port) == 0) { /* found USBasp with old IDs */ pmsg_error("found USB device USBasp with old VID/PID; please update firmware of USBasp\n"); return 0; From fa2e69de57c738ab1567000ab6d4d33862496ca5 Mon Sep 17 00:00:00 2001 From: MikeRaffa Date: Mon, 4 Dec 2023 17:20:03 -0500 Subject: [PATCH 2/4] Updates based on feedback from stefanrueger. Change sense of check_for_port_argument_match() to return nonzero when there is a match. Increase size of 'bus_num' and 'dev_addr' from 4 to 21. Various cleanup, formatting changes, and simplifications. Match trailing end of serial numbers. --- src/usbasp.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/usbasp.c b/src/usbasp.c index 4971a23e..4705e530 100644 --- a/src/usbasp.c +++ b/src/usbasp.c @@ -410,25 +410,16 @@ static int check_for_port_argument_match(const char *port, char *bus, char *devi char *colon_pointer = strchr(port, ':'); if (colon_pointer) { // Value contains ':' character. Compare with bus/device. - if (strncmp(port, bus, colon_pointer - port)) { - return 1; - } - port = colon_pointer + 1; - if (strcmp(port, device)) { - return 1; - } - return 0; - } else { - // serial number case - if (!str_eq(serial_num, port)) { - return 1; - } else { + if (strncmp(port, bus, colon_pointer - port)) return 0; - } + port = colon_pointer + 1; + return str_eq(port, device); } + // serial number case + return (*port && str_ends(serial_num, port)); } // Invalid -P option. - return 1; + return 0; } /* @@ -498,11 +489,11 @@ static int usbOpenDevice(libusb_device_handle **device, int vendor, const char * if(!str_eq(port, "usb")) { // -P option given libusb_get_string_descriptor_ascii(handle, descriptor.iSerialNumber, (unsigned char*)string, sizeof(string)); - char bus_num[4]; + char bus_num[21]; sprintf(bus_num, "%d", libusb_get_bus_number(dev)); - char dev_addr[4]; + char dev_addr[21]; sprintf(dev_addr, "%d", libusb_get_device_address(dev)); - if (check_for_port_argument_match(port, bus_num, dev_addr, string)) { + if (!check_for_port_argument_match(port, bus_num, dev_addr, string)) { errorCode = USB_ERROR_NOTFOUND; } } @@ -582,7 +573,7 @@ static int didUsbInit = 0; // -P option given usb_get_string_simple(handle, dev->descriptor.iSerialNumber, string, sizeof(string)); - if (check_for_port_argument_match(port, bus->dirname, dev->filename, string)) { + if (!check_for_port_argument_match(port, bus->dirname, dev->filename, string)) { errorCode = USB_ERROR_NOTFOUND; } } From e351dc30b2d7c77775e27408568b0dc167caf798 Mon Sep 17 00:00:00 2001 From: MikeRaffa Date: Mon, 4 Dec 2023 17:29:38 -0500 Subject: [PATCH 3/4] Documentation for Issue #973 changes. --- src/doc/avrdude.texi | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/doc/avrdude.texi b/src/doc/avrdude.texi index f3f34834..bd11cd4c 100644 --- a/src/doc/avrdude.texi +++ b/src/doc/avrdude.texi @@ -739,8 +739,11 @@ and Mac OS but not on Windows. For more information about AVR-Doper see For the USBtinyISP, which is a simplistic device not implementing serial numbers, multiple devices can be distinguished by their location in the USB hierarchy. -See the respective -@xref{Troubleshooting} entry for examples. +See the respective @ref{Troubleshooting} entry for examples. + +For USBasp, multiple devices can be distinguished by either USB connection +or serial number. +See the respective @ref{Troubleshooting} entry for examples. For the XBee programmer the target MCU is to be programmed wirelessly over a ZigBee mesh using the XBeeBoot bootloader. The ZigBee 64-bit @@ -4233,6 +4236,14 @@ avrdude -c usbtiny -p atmega8 \ -P usb:bus-0:\\.\libusb0-0001--0x1781-0x0c9f (Windows) @end example +For USBasp, the same format for @var{-P usb} can be used to match usb bus/device. Alternatively, +device serial number can be specified as follows (for serial number '1234'). + +@example +avrdude -c USBasp -p atmega8 -P usb:1234 +@end example + + @item Problem: I cannot do @dots{} when the target is in debugWire mode. From 5dbbb4e6c580cc2161a14b20e9b345e870b50a4b Mon Sep 17 00:00:00 2001 From: MikeRaffa Date: Wed, 6 Dec 2023 20:03:31 -0500 Subject: [PATCH 4/4] Remove check for 'verbose' on call to pmsg_notice(). Remove some unnecessary braces and parens. --- src/usbasp.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/usbasp.c b/src/usbasp.c index 4705e530..f5c301c8 100644 --- a/src/usbasp.c +++ b/src/usbasp.c @@ -401,9 +401,7 @@ static int usbasp_transmit(const PROGRAMMER *pgm, static int check_for_port_argument_match(const char *port, char *bus, char *device, char *serial_num) { - if (verbose) { - pmsg_notice("usbOpenDevice(): found USBasp, bus:device: %s:%s, serial_number: %s\n", bus, device, serial_num); - } + pmsg_notice("usbOpenDevice(): found USBasp, bus:device: %s:%s, serial_number: %s\n", bus, device, serial_num); const size_t usb_len = strlen("usb"); if(str_starts(port, "usb") && ':' == port[usb_len]) { port += usb_len + 1; @@ -416,7 +414,7 @@ static int check_for_port_argument_match(const char *port, char *bus, char *devi return str_eq(port, device); } // serial number case - return (*port && str_ends(serial_num, port)); + return *port && str_ends(serial_num, port); } // Invalid -P option. return 0; @@ -493,9 +491,8 @@ static int usbOpenDevice(libusb_device_handle **device, int vendor, const char * sprintf(bus_num, "%d", libusb_get_bus_number(dev)); char dev_addr[21]; sprintf(dev_addr, "%d", libusb_get_device_address(dev)); - if (!check_for_port_argument_match(port, bus_num, dev_addr, string)) { + if (!check_for_port_argument_match(port, bus_num, dev_addr, string)) errorCode = USB_ERROR_NOTFOUND; - } } } if (errorCode == 0) @@ -573,9 +570,8 @@ static int didUsbInit = 0; // -P option given usb_get_string_simple(handle, dev->descriptor.iSerialNumber, string, sizeof(string)); - if (!check_for_port_argument_match(port, bus->dirname, dev->filename, string)) { + if (!check_for_port_argument_match(port, bus->dirname, dev->filename, string)) errorCode = USB_ERROR_NOTFOUND; - } } } if (errorCode == 0)