host: Allow matching of device to USB path, not just address

USB addresses change every time the device re-enumerates, while the path
reflects the physical topology of USB connections and stays persistent
unless the usb cabling is changed.  Let's allow the user to specify the
path instead of the address to uniquely identify a given slot.
diff --git a/host/libusb_util.c b/host/libusb_util.c
index 8148660..3236e41 100644
--- a/host/libusb_util.c
+++ b/host/libusb_util.c
@@ -3,11 +3,33 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <string.h>
 
 #include <libusb.h>
 
 #include "libusb_util.h"
 
+static char path_buf[USB_MAX_PATH_LEN];
+
+static char *get_path(libusb_device *dev)
+{
+#if (defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102) || (defined(LIBUSBX_API_VERSION) && LIBUSBX_API_VERSION >= 0x01000102)
+	uint8_t path[8];
+	int r,j;
+	r = libusb_get_port_numbers(dev, path, sizeof(path));
+	if (r > 0) {
+		sprintf(path_buf,"%d-%d",libusb_get_bus_number(dev),path[0]);
+		for (j = 1; j < r; j++){
+			sprintf(path_buf+strlen(path_buf),".%d",path[j]);
+		};
+	}
+	return path_buf;
+#else
+# warning "libusb too old - building without USB path support!"
+	return NULL;
+#endif
+}
+
 static int match_dev_id(const struct libusb_device_descriptor *desc, const struct dev_id *id)
 {
 	if ((desc->idVendor == id->vendor_id) && (desc->idProduct == id->product_id))
@@ -79,6 +101,7 @@
 	struct libusb_device_descriptor dev_desc;
 	int rc, i, out_idx = 0;
 	uint8_t addr;
+	char *path;
 
 	rc = libusb_get_device_descriptor(dev, &dev_desc);
 	if (rc < 0) {
@@ -87,6 +110,7 @@
 	}
 
 	addr = libusb_get_device_address(dev);
+	path = get_path(dev);
 
 	/* iterate over all configurations */
 	for (i = 0; i < dev_desc.bNumConfigurations; i++) {
@@ -117,6 +141,8 @@
 				out[out_idx].vendor = dev_desc.idVendor;
 				out[out_idx].product = dev_desc.idProduct;
 				out[out_idx].addr = addr;
+				strncpy(out[out_idx].path, path, sizeof(out[out_idx].path));
+				out[out_idx].path[sizeof(out[out_idx].path)-1] = '\0';
 				out[out_idx].configuration = conf_desc->bConfigurationValue;
 				out[out_idx].interface = if_desc->bInterfaceNumber;
 				out[out_idx].altsetting = if_desc->bAlternateSetting;
@@ -198,9 +224,12 @@
 
 	for (dev = list; *dev; dev++) {
 		int addr;
+		char *path;
 
 		addr = libusb_get_device_address(*dev);
-		if (addr == ifm->addr) {
+		path = get_path(*dev);
+		if ((ifm->addr && addr == ifm->addr) ||
+		    (strlen(ifm->path) && !strcmp(path, ifm->path))) {
 			rc = libusb_open(*dev, &usb_devh);
 			if (rc < 0) {
 				perror("Cannot open device");
diff --git a/host/libusb_util.h b/host/libusb_util.h
index fc780b1..5bbe3cb 100644
--- a/host/libusb_util.h
+++ b/host/libusb_util.h
@@ -2,6 +2,8 @@
 
 #include <libusb.h>
 
+#define USB_MAX_PATH_LEN 20
+
 struct dev_id {
 	uint16_t vendor_id;
 	uint16_t product_id;
@@ -21,6 +23,8 @@
 	uint16_t product;
 	/* USB Bus Address */
 	uint8_t addr;
+	/* physical path */
+	char path[USB_MAX_PATH_LEN];
 	/* configuration of matching interface */
 	uint8_t configuration;
 	/* interface number of matching interface */
diff --git a/host/simtrace2-remsim.c b/host/simtrace2-remsim.c
index 67cf8b8..df38f77 100644
--- a/host/simtrace2-remsim.c
+++ b/host/simtrace2-remsim.c
@@ -522,6 +522,7 @@
 	{ "usb-interface", 1, 0, 'I' },
 	{ "usb-altsetting", 1, 0, 'S' },
 	{ "usb-address", 1, 0, 'A' },
+	{ "usb-path", 1, 0, 'H' },
 	{ NULL, 0, 0, 0 }
 };
 
@@ -601,6 +602,7 @@
 	int if_num = 0, vendor_id = -1, product_id = -1;
 	int config_id = -1, altsetting = 0, addr = -1;
 	char *remote_udp_host = NULL;
+	char *path = NULL;
 	struct osim_reader_hdl *reader;
 	struct osim_card_hdl *card;
 
@@ -609,7 +611,7 @@
 	while (1) {
 		int option_index = 0;
 
-		c = getopt_long(argc, argv, "r:p:hi:V:P:C:I:S:A:ak", opts, &option_index);
+		c = getopt_long(argc, argv, "r:p:hi:V:P:C:I:S:A:H:ak", opts, &option_index);
 		if (c == -1)
 			break;
 		switch (c) {
@@ -650,6 +652,9 @@
 		case 'A':
 			addr = atoi(optarg);
 			break;
+		case 'H':
+			path = optarg;
+			break;
 		}
 	}
 
@@ -714,6 +719,8 @@
 			ifm->interface = if_num;
 			ifm->altsetting = altsetting;
 			ifm->addr = addr;
+			if (path)
+				osmo_strlcpy(ifm->path, path, sizeof(ifm->path));
 			transp->usb_devh = usb_open_claim_interface(NULL, ifm);
 			if (!transp->usb_devh) {
 				fprintf(stderr, "can't open USB device\n");
diff --git a/host/simtrace2_usb.c b/host/simtrace2_usb.c
index ad4b73b..6cbb8b7 100644
--- a/host/simtrace2_usb.c
+++ b/host/simtrace2_usb.c
@@ -41,8 +41,8 @@
 		libusb_device_handle *dev_handle;
 		char strbuf[256];
 
-		printf("\t%04x:%04x Addr=%u, Cfg=%u, Intf=%u, Alt=%u: %d/%d/%d ",
-			m->vendor, m->product, m->addr,
+		printf("\t%04x:%04x Addr=%u, Path=%s, Cfg=%u, Intf=%u, Alt=%u: %d/%d/%d ",
+			m->vendor, m->product, m->addr, m->path,
 			m->configuration, m->interface, m->altsetting,
 			m->class, m->sub_class, m->protocol);