usbdev: Extract libusb1 device discovery into a separate file

Currently there is a TODO-like comment in the dediprog driver: "Might be
useful for other USB devices as well". Act on this comment by collecting
all the device discovery code for libusb1 devices into a separate file.

Change-Id: Idfcc79371241c2c1dea97faf5e532aa971546a79
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
Reviewed-on: https://review.coreboot.org/27443
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/Makefile b/Makefile
index ddfd711..b13cf7e 100644
--- a/Makefile
+++ b/Makefile
@@ -1023,6 +1023,7 @@
 ifneq ($(NEED_LIBUSB1), )
 CHECK_LIBUSB1 = yes
 FEATURE_CFLAGS += -D'NEED_LIBUSB1=1'
+PROGRAMMER_OBJS += usbdev.o
 # FreeBSD and DragonflyBSD use a reimplementation of libusb-1.0 that is simply called libusb
 ifeq ($(TARGET_OS),$(filter $(TARGET_OS),FreeBSD DragonFlyBSD))
 USB1LIBS += -lusb
diff --git a/dediprog.c b/dediprog.c
index 7fcadfb..1a469a7 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -242,50 +242,6 @@
 }
 
 
-/* Might be useful for other USB devices as well. static for now.
- * num parameter allows user to specify one device of multiple installed */
-static struct libusb_device_handle *get_device_by_vid_pid_number(uint16_t vid, uint16_t pid, unsigned int num)
-{
-	struct libusb_device **list;
-	ssize_t count = libusb_get_device_list(usb_ctx, &list);
-	if (count < 0) {
-		msg_perr("Getting the USB device list failed (%s)!\n", libusb_error_name(count));
-		return NULL;
-	}
-
-	struct libusb_device_handle *handle = NULL;
-	ssize_t i = 0;
-	for (i = 0; i < count; i++) {
-		struct libusb_device *dev = list[i];
-		struct libusb_device_descriptor desc;
-		int err = libusb_get_device_descriptor(dev, &desc);
-		if (err != 0) {
-			msg_perr("Reading the USB device descriptor failed (%s)!\n", libusb_error_name(err));
-			libusb_free_device_list(list, 1);
-			return NULL;
-		}
-		if ((desc.idVendor == vid) && (desc.idProduct == pid)) {
-			msg_pdbg("Found USB device %04"PRIx16":%04"PRIx16" at address %d-%d.\n",
-				 desc.idVendor, desc.idProduct,
-				 libusb_get_bus_number(dev), libusb_get_device_address(dev));
-			if (num == 0) {
-				err = libusb_open(dev, &handle);
-				if (err != 0) {
-					msg_perr("Opening the USB device failed (%s)!\n",
-						 libusb_error_name(err));
-					libusb_free_device_list(list, 1);
-					return NULL;
-				}
-				break;
-			}
-			num--;
-		}
-	}
-	libusb_free_device_list(list, 1);
-
-	return handle;
-}
-
 /* This function sets the GPIOs connected to the LEDs as well as IO1-IO4. */
 static int dediprog_set_leds(int leds)
 {
@@ -1102,7 +1058,7 @@
 
 	const uint16_t vid = devs_dediprog[0].vendor_id;
 	const uint16_t pid = devs_dediprog[0].device_id;
-	dediprog_handle = get_device_by_vid_pid_number(vid, pid, (unsigned int) usedevice);
+	dediprog_handle = usb_dev_get_by_vid_pid_number(usb_ctx, vid, pid, (unsigned int) usedevice);
 	if (!dediprog_handle) {
 		msg_perr("Could not find a Dediprog programmer on USB.\n");
 		libusb_exit(usb_ctx);
diff --git a/developerbox_spi.c b/developerbox_spi.c
index 8482dea..4ad966e 100644
--- a/developerbox_spi.c
+++ b/developerbox_spi.c
@@ -34,7 +34,6 @@
 #include "platform.h"
 
 #include <stdlib.h>
-#include <string.h>
 #include <libusb.h>
 #include "programmer.h"
 #include "spi.h"
@@ -130,67 +129,6 @@
 	.set_sck_set_mosi = cp210x_bitbang_set_sck_set_mosi,
 };
 
-static struct libusb_device_handle *get_device_by_vid_pid_serial(uint16_t vid, uint16_t pid,
-								 const char *serialno)
-{
-	struct libusb_device **list;
-	ssize_t count = libusb_get_device_list(usb_ctx, &list);
-	if (count < 0) {
-		msg_perr("Getting the USB device list failed (%s)!\n", libusb_error_name(count));
-		return NULL;
-	}
-
-	ssize_t i = 0;
-	for (i = 0; i < count; i++) {
-		struct libusb_device *dev = list[i];
-		struct libusb_device_descriptor desc;
-		struct libusb_device_handle *handle;
-
-		int res = libusb_get_device_descriptor(dev, &desc);
-		if (res != 0) {
-			msg_perr("Reading the USB device descriptor failed (%s)!\n", libusb_error_name(res));
-			continue;
-		}
-
-		if ((desc.idVendor != vid) && (desc.idProduct != pid))
-			continue;
-
-		msg_pdbg("Found USB device %04"PRIx16":%04"PRIx16" at address %d-%d.\n",
-			 desc.idVendor, desc.idProduct,
-			 libusb_get_bus_number(dev), libusb_get_device_address(dev));
-
-		res = libusb_open(dev, &handle);
-		if (res != 0) {
-			msg_perr("Opening the USB device failed (%s)!\n", libusb_error_name(res));
-			continue;
-		}
-
-		if (serialno) {
-			unsigned char myserial[64];
-			res = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, myserial,
-								 sizeof(myserial));
-			if (res < 0) {
-				msg_perr("Reading the USB serialno failed (%s)!\n", libusb_error_name(res));
-				libusb_close(handle);
-				continue;
-			}
-			msg_pdbg("Serial number is %s\n", myserial);
-
-			/* Filter out any serial number that does not commence with serialno */
-			if (0 != strncmp(serialno, (char *) myserial, strlen(serialno))) {
-				libusb_close(handle);
-				continue;
-			}
-		}
-
-		libusb_free_device_list(list, 1);
-		return handle;
-	}
-
-	libusb_free_device_list(list, 1);
-	return NULL;
-}
-
 static int developerbox_spi_shutdown(void *data)
 {
 	libusb_close(cp210x_handle);
@@ -210,7 +148,7 @@
 	char *serialno = extract_programmer_param("serial");
 	if (serialno)
 		msg_pdbg("Looking for serial number commencing %s\n", serialno);
-	cp210x_handle = get_device_by_vid_pid_serial(
+	cp210x_handle = usb_dev_get_by_vid_pid_serial(usb_ctx,
 			devs_developerbox_spi[0].vendor_id, devs_developerbox_spi[0].device_id, serialno);
 	free(serialno);
 	if (!cp210x_handle) {
diff --git a/programmer.h b/programmer.h
index 300cf5f..311992a 100644
--- a/programmer.h
+++ b/programmer.h
@@ -841,4 +841,12 @@
 		flash->mst->spi.features & SPI_MASTER_4BA;
 }
 
+/* usbdev.c */
+struct libusb_device_handle;
+struct libusb_context;
+struct libusb_device_handle *usb_dev_get_by_vid_pid_serial(
+		struct libusb_context *usb_ctx, uint16_t vid, uint16_t pid, const char *serialno);
+struct libusb_device_handle *usb_dev_get_by_vid_pid_number(
+		struct libusb_context *usb_ctx, uint16_t vid, uint16_t pid, unsigned int num);
+
 #endif				/* !__PROGRAMMER_H__ */
diff --git a/usbdev.c b/usbdev.c
new file mode 100644
index 0000000..5c34ba1
--- /dev/null
+++ b/usbdev.c
@@ -0,0 +1,130 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2016 secunet Security Networks AG
+ * Copyright (C) 2018 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <string.h>
+#include <libusb.h>
+#include "programmer.h"
+
+struct libusb_device_handle *usb_dev_get_by_vid_pid_serial(
+		struct libusb_context *usb_ctx, uint16_t vid, uint16_t pid, const char *serialno)
+{
+	struct libusb_device **list;
+	ssize_t count = libusb_get_device_list(usb_ctx, &list);
+	if (count < 0) {
+		msg_perr("Getting the USB device list failed (%s)!\n", libusb_error_name(count));
+		return NULL;
+	}
+
+	ssize_t i = 0;
+	for (i = 0; i < count; i++) {
+		struct libusb_device *dev = list[i];
+		struct libusb_device_descriptor desc;
+		struct libusb_device_handle *handle;
+
+		int res = libusb_get_device_descriptor(dev, &desc);
+		if (res != 0) {
+			msg_perr("Reading the USB device descriptor failed (%s)!\n", libusb_error_name(res));
+			continue;
+		}
+
+		if ((desc.idVendor != vid) && (desc.idProduct != pid))
+			continue;
+
+		msg_pdbg("Found USB device %04"PRIx16":%04"PRIx16" at address %d-%d.\n",
+			 desc.idVendor, desc.idProduct,
+			 libusb_get_bus_number(dev), libusb_get_device_address(dev));
+
+		res = libusb_open(dev, &handle);
+		if (res != 0) {
+			msg_perr("Opening the USB device failed (%s)!\n", libusb_error_name(res));
+			continue;
+		}
+
+		if (serialno) {
+			unsigned char myserial[64];
+			res = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, myserial,
+					sizeof(myserial));
+			if (res < 0) {
+				msg_perr("Reading the USB serialno failed (%s)!\n", libusb_error_name(res));
+				libusb_close(handle);
+				continue;
+			}
+			msg_pdbg("Serial number is %s\n", myserial);
+
+			/* Reject any serial number that does not commence with serialno */
+			if (0 != strncmp(serialno, (char *)myserial, strlen(serialno))) {
+				libusb_close(handle);
+				continue;
+			}
+		}
+
+		libusb_free_device_list(list, 1);
+		return handle;
+	}
+
+	libusb_free_device_list(list, 1);
+	return NULL;
+}
+
+/*
+ * This function allows different devices to be targeted based on enumeration order. Different
+ * hotplug sequencing (or simply a reboot) may change the enumeration order. This function should
+ * only be used if a programmers does not provide an alternative way to identify itself uniquely
+ * (such as a unique serial number).
+ */
+struct libusb_device_handle *usb_dev_get_by_vid_pid_number(
+		struct libusb_context *usb_ctx, uint16_t vid, uint16_t pid, unsigned int num)
+{
+	struct libusb_device **list;
+	ssize_t count = libusb_get_device_list(usb_ctx, &list);
+	if (count < 0) {
+		msg_perr("Getting the USB device list failed (%s)!\n", libusb_error_name(count));
+		return NULL;
+	}
+
+	struct libusb_device_handle *handle = NULL;
+	ssize_t i = 0;
+	for (i = 0; i < count; i++) {
+		struct libusb_device *dev = list[i];
+		struct libusb_device_descriptor desc;
+		int err = libusb_get_device_descriptor(dev, &desc);
+		if (err != 0) {
+			msg_perr("Reading the USB device descriptor failed (%s)!\n", libusb_error_name(err));
+			libusb_free_device_list(list, 1);
+			return NULL;
+		}
+		if ((desc.idVendor == vid) && (desc.idProduct == pid)) {
+			msg_pdbg("Found USB device %04"PRIx16":%04"PRIx16" at address %d-%d.\n",
+				 desc.idVendor, desc.idProduct,
+				 libusb_get_bus_number(dev), libusb_get_device_address(dev));
+			if (num == 0) {
+				err = libusb_open(dev, &handle);
+				if (err != 0) {
+					msg_perr("Opening the USB device failed (%s)!\n",
+						 libusb_error_name(err));
+					libusb_free_device_list(list, 1);
+					return NULL;
+				}
+				break;
+			}
+			num--;
+		}
+	}
+	libusb_free_device_list(list, 1);
+
+	return handle;
+}