From 487c57d9a27244b6a916cce253d8aaaec5e22604 Mon Sep 17 00:00:00 2001 From: Paul Fertser Date: Wed, 1 Oct 2014 10:36:02 +0400 Subject: [PATCH] libusb: introduce jtag_libusb_choose_interface() and use it for JLink This introduces a new common function that allows auto-discovery of a suitable USB interface based on class, subclass and protocol matching. It claims the interface and returns the corresponding endpoints number to the caller. The need for this arised due to nRF51822 USB dongle which comes with an "on-board Segger J-link debugger" having 3 interfaces, so the current code can't work at all with it (in this particular case the last interface needs to be choosen). This also removes special handling of JLink-OB endpoint numbers as it's now possible to autodetect them as well as the standard JLink endpoints. Change-Id: I4d990a7a3b373efdd2949a394b32d855a168e138 Signed-off-by: Paul Fertser Reviewed-on: http://openocd.zylin.com/2327 Tested-by: jenkins Reviewed-by: Spencer Oliver --- src/jtag/aice/aice_usb.c | 4 +--- src/jtag/drivers/jlink.c | 30 +++++++---------------- src/jtag/drivers/libusb0_common.c | 21 +++++++++++++--- src/jtag/drivers/libusb0_common.h | 5 ++-- src/jtag/drivers/libusb1_common.c | 50 +++++++++++++++++++++++++-------------- src/jtag/drivers/libusb1_common.h | 18 ++++++++++++-- 6 files changed, 79 insertions(+), 49 deletions(-) diff --git a/src/jtag/aice/aice_usb.c b/src/jtag/aice/aice_usb.c index d933845c5..50b3b9a22 100644 --- a/src/jtag/aice/aice_usb.c +++ b/src/jtag/aice/aice_usb.c @@ -2136,13 +2136,11 @@ static int aice_usb_open(struct aice_port_param_s *param) #endif /* usb_set_configuration required under win32 */ - struct jtag_libusb_device *udev = jtag_libusb_get_device(devh); jtag_libusb_set_configuration(devh, 0); - jtag_libusb_claim_interface(devh, 0); unsigned int aice_read_ep; unsigned int aice_write_ep; - jtag_libusb_get_endpoints(udev, &aice_read_ep, &aice_write_ep); + jtag_libusb_choose_interface(devh, &aice_read_ep, &aice_write_ep, -1, -1, -1); aice_handler.usb_read_ep = aice_read_ep; aice_handler.usb_write_ep = aice_write_ep; diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c index 2177b08e0..57eea64d1 100644 --- a/src/jtag/drivers/jlink.c +++ b/src/jtag/drivers/jlink.c @@ -47,16 +47,12 @@ * pid = ( usb_address > 0x4) ? 0x0101 : (0x101 + usb_address) */ -#define JLINK_OB_PID 0x0105 +#define JLINK_USB_INTERFACE_CLASS 0xff +#define JLINK_USB_INTERFACE_SUBCLASS 0xff +#define JLINK_USB_INTERFACE_PROTOCOL 0xff -#define JLINK_WRITE_ENDPOINT 0x02 -#define JLINK_READ_ENDPOINT 0x81 - -#define JLINK_OB_WRITE_ENDPOINT 0x06 -#define JLINK_OB_READ_ENDPOINT 0x85 - -static unsigned int jlink_write_ep = JLINK_WRITE_ENDPOINT; -static unsigned int jlink_read_ep = JLINK_READ_ENDPOINT; +static unsigned int jlink_write_ep; +static unsigned int jlink_read_ep; static unsigned int jlink_hw_jtag_version = 2; #define JLINK_USB_TIMEOUT 1000 @@ -1746,20 +1742,12 @@ static struct jlink *jlink_usb_open() /* usb_set_configuration is only required under win32 * with libusb 0.1 and libusb0.sys. For libusb 1.0 it is a no-op * since the configuration is already set. */ - struct jtag_libusb_device *udev = jtag_libusb_get_device(devh); jtag_libusb_set_configuration(devh, 0); - jtag_libusb_claim_interface(devh, 0); - - /* Use the OB endpoints if the JLink we matched is a Jlink-OB adapter */ - uint16_t matched_pid; - if (jtag_libusb_get_pid(udev, &matched_pid) == ERROR_OK) { - if (matched_pid == JLINK_OB_PID) { - jlink_read_ep = JLINK_OB_WRITE_ENDPOINT; - jlink_write_ep = JLINK_OB_READ_ENDPOINT; - } - } - jtag_libusb_get_endpoints(udev, &jlink_read_ep, &jlink_write_ep); + jtag_libusb_choose_interface(devh, &jlink_read_ep, &jlink_write_ep, + JLINK_USB_INTERFACE_CLASS, + JLINK_USB_INTERFACE_SUBCLASS, + JLINK_USB_INTERFACE_PROTOCOL); struct jlink *result = malloc(sizeof(struct jlink)); result->usb_handle = devh; diff --git a/src/jtag/drivers/libusb0_common.c b/src/jtag/drivers/libusb0_common.c index b44b7861b..5b1248d26 100644 --- a/src/jtag/drivers/libusb0_common.c +++ b/src/jtag/drivers/libusb0_common.c @@ -145,14 +145,23 @@ int jtag_libusb_set_configuration(jtag_libusb_device_handle *devh, udev->config[configuration].bConfigurationValue); } -int jtag_libusb_get_endpoints(struct jtag_libusb_device *udev, +int jtag_libusb_choose_interface(struct jtag_libusb_device_handle *devh, unsigned int *usb_read_ep, - unsigned int *usb_write_ep) + unsigned int *usb_write_ep, + int bclass, int subclass, int protocol) { + struct jtag_libusb_device *udev = jtag_libusb_get_device(devh); struct usb_interface *iface = udev->config->interface; struct usb_interface_descriptor *desc = iface->altsetting; + *usb_read_ep = *usb_write_ep = 0; + for (int i = 0; i < desc->bNumEndpoints; i++) { + if ((bclass > 0 && desc->bInterfaceClass != bclass) || + (subclass > 0 && desc->bInterfaceSubClass != subclass) || + (protocol > 0 && desc->bInterfaceProtocol != protocol)) + continue; + uint8_t epnum = desc->endpoint[i].bEndpointAddress; bool is_input = epnum & 0x80; LOG_DEBUG("usb ep %s %02x", is_input ? "in" : "out", epnum); @@ -160,9 +169,15 @@ int jtag_libusb_get_endpoints(struct jtag_libusb_device *udev, *usb_read_ep = epnum; else *usb_write_ep = epnum; + + if (*usb_read_ep && *usb_write_ep) { + LOG_DEBUG("Claiming interface %d", (int)desc->bInterfaceNumber); + usb_claim_interface(devh, (int)desc->bInterfaceNumber); + return ERROR_OK; + } } - return 0; + return ERROR_FAIL; } int jtag_libusb_get_pid(struct jtag_libusb_device *dev, uint16_t *pid) diff --git a/src/jtag/drivers/libusb0_common.h b/src/jtag/drivers/libusb0_common.h index 370954fd3..ca372a080 100644 --- a/src/jtag/drivers/libusb0_common.h +++ b/src/jtag/drivers/libusb0_common.h @@ -66,9 +66,10 @@ int jtag_libusb_bulk_read(struct jtag_libusb_device_handle *dev, int ep, char *bytes, int size, int timeout); int jtag_libusb_set_configuration(jtag_libusb_device_handle *devh, int configuration); -int jtag_libusb_get_endpoints(struct jtag_libusb_device *udev, +int jtag_libusb_choose_interface(struct jtag_libusb_device_handle *devh, unsigned int *usb_read_ep, - unsigned int *usb_write_ep); + unsigned int *usb_write_ep, + int bclass, int subclass, int protocol); int jtag_libusb_get_pid(struct jtag_libusb_device *dev, uint16_t *pid); #endif /* JTAG_USB_COMMON_H */ diff --git a/src/jtag/drivers/libusb1_common.c b/src/jtag/drivers/libusb1_common.c index 07d9f9512..bda91ffbc 100644 --- a/src/jtag/drivers/libusb1_common.c +++ b/src/jtag/drivers/libusb1_common.c @@ -186,40 +186,54 @@ int jtag_libusb_set_configuration(jtag_libusb_device_handle *devh, return retCode; } -int jtag_libusb_get_endpoints(struct jtag_libusb_device *udev, +int jtag_libusb_choose_interface(struct jtag_libusb_device_handle *devh, unsigned int *usb_read_ep, - unsigned int *usb_write_ep) + unsigned int *usb_write_ep, + int bclass, int subclass, int protocol) { + struct jtag_libusb_device *udev = jtag_libusb_get_device(devh); const struct libusb_interface *inter; const struct libusb_interface_descriptor *interdesc; const struct libusb_endpoint_descriptor *epdesc; struct libusb_config_descriptor *config; + *usb_read_ep = *usb_write_ep = 0; + libusb_get_config_descriptor(udev, 0, &config); for (int i = 0; i < (int)config->bNumInterfaces; i++) { inter = &config->interface[i]; - for (int j = 0; j < inter->num_altsetting; j++) { - interdesc = &inter->altsetting[j]; - for (int k = 0; - k < (int)interdesc->bNumEndpoints; k++) { - epdesc = &interdesc->endpoint[k]; - - uint8_t epnum = epdesc->bEndpointAddress; - bool is_input = epnum & 0x80; - LOG_DEBUG("usb ep %s %02x", - is_input ? "in" : "out", epnum); - - if (is_input) - *usb_read_ep = epnum; - else - *usb_write_ep = epnum; + interdesc = &inter->altsetting[0]; + for (int k = 0; + k < (int)interdesc->bNumEndpoints; k++) { + if ((bclass > 0 && interdesc->bInterfaceClass != bclass) || + (subclass > 0 && interdesc->bInterfaceSubClass != subclass) || + (protocol > 0 && interdesc->bInterfaceProtocol != protocol)) + continue; + + epdesc = &interdesc->endpoint[k]; + + uint8_t epnum = epdesc->bEndpointAddress; + bool is_input = epnum & 0x80; + LOG_DEBUG("usb ep %s %02x", + is_input ? "in" : "out", epnum); + + if (is_input) + *usb_read_ep = epnum; + else + *usb_write_ep = epnum; + + if (*usb_read_ep && *usb_write_ep) { + LOG_DEBUG("Claiming interface %d", (int)interdesc->bInterfaceNumber); + libusb_claim_interface(devh, (int)interdesc->bInterfaceNumber); + libusb_free_config_descriptor(config); + return ERROR_OK; } } } libusb_free_config_descriptor(config); - return 0; + return ERROR_FAIL; } int jtag_libusb_get_pid(struct jtag_libusb_device *dev, uint16_t *pid) diff --git a/src/jtag/drivers/libusb1_common.h b/src/jtag/drivers/libusb1_common.h index a4447ca9b..4ef6bf730 100644 --- a/src/jtag/drivers/libusb1_common.h +++ b/src/jtag/drivers/libusb1_common.h @@ -60,9 +60,23 @@ int jtag_libusb_bulk_read(struct jtag_libusb_device_handle *dev, int ep, char *bytes, int size, int timeout); int jtag_libusb_set_configuration(jtag_libusb_device_handle *devh, int configuration); -int jtag_libusb_get_endpoints(struct jtag_libusb_device *udev, +/** + * Find the first interface optionally matching class, subclass and + * protocol and claim it. + * @param devh _libusb_ device handle. + * @param usb_read_ep A pointer to a variable where the _IN_ endpoint + * number will be stored. + * @param usb_write_ep A pointer to a variable where the _OUT_ endpoint + * number will be stored. + * @param bclass `bInterfaceClass` to match, or -1 to ignore this field. + * @param subclass `bInterfaceSubClass` to match, or -1 to ignore this field. + * @param protocol `bInterfaceProtocol` to match, or -1 to ignore this field. + * @returns Returns ERROR_OK on success, ERROR_FAIL otherwise. + */ +int jtag_libusb_choose_interface(struct jtag_libusb_device_handle *devh, unsigned int *usb_read_ep, - unsigned int *usb_write_ep); + unsigned int *usb_write_ep, + int bclass, int subclass, int protocol); int jtag_libusb_get_pid(struct jtag_libusb_device *dev, uint16_t *pid); #endif /* JTAG_USB_COMMON_H */ -- 2.11.4.GIT