ch347_spi: Search for compatible USB interface

The newer CH347F version uses a different interface number. Hence,
look for the interface with "vendor specific" class, which is what
the SPI interface uses.

Tested with the original CH347T in mode 1 and upcoming CH347F.

Change-Id: I16d66b2562d9d2ec1540949d63752e939540db5d
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/197
Reviewed-by: Nicholas Chin <nic.c3.14@gmail.com>
diff --git a/ch347_spi.c b/ch347_spi.c
index 27e3374..822f7a9 100644
--- a/ch347_spi.c
+++ b/ch347_spi.c
@@ -36,9 +36,6 @@
 #define WRITE_EP	0x06
 #define READ_EP 	0x86
 
-#define MODE_1_IFACE 2
-#define MODE_2_IFACE 1
-
 /* The USB descriptor says the max transfer size is 512 bytes, but the
  * vendor driver only seems to transfer a maximum of 510 bytes at once,
  * leaving 507 bytes for data as the command + length take up 3 bytes
@@ -48,6 +45,7 @@
 
 struct ch347_spi_data {
 	struct libusb_device_handle *handle;
+	unsigned int iface;
 };
 
 /* TODO: Add support for HID mode */
@@ -60,10 +58,8 @@
 {
 	struct ch347_spi_data *ch347_data = data;
 
-	/* TODO: Set this depending on the mode */
-	int spi_interface = MODE_1_IFACE;
-	libusb_release_interface(ch347_data->handle, spi_interface);
-	libusb_attach_kernel_driver(ch347_data->handle, spi_interface);
+	libusb_release_interface(ch347_data->handle, ch347_data->iface);
+	libusb_attach_kernel_driver(ch347_data->handle, ch347_data->iface);
 	libusb_close(ch347_data->handle);
 	libusb_exit(NULL);
 
@@ -322,16 +318,32 @@
 		return 1;
 	}
 
-	/* TODO: set based on mode */
-	/* Mode 1 uses interface 2 for the SPI interface */
-	int spi_interface = MODE_1_IFACE;
+	struct libusb_config_descriptor *config;
+	ret = libusb_get_active_config_descriptor(libusb_get_device(ch347_data->handle), &config);
+	if (ret != LIBUSB_SUCCESS) {
+		msg_perr("Couldn't get config descriptor: %s (%d)\n", libusb_strerror(ret), ret);
+		ret = 1;
+		goto error_exit;
+	}
 
-	ret = libusb_detach_kernel_driver(ch347_data->handle, spi_interface);
+	unsigned int iface;
+	for (iface = 0; iface < config->bNumInterfaces; ++iface) {
+		if (config->interface[iface].altsetting[0].bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC)
+			break;
+	}
+	if (iface == config->bNumInterfaces) {
+		msg_perr("Couldn't find compatible interface.\n");
+		ret = 1;
+		goto error_exit;
+	}
+	ch347_data->iface = iface;
+
+	ret = libusb_detach_kernel_driver(ch347_data->handle, iface);
 	if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND)
 		msg_pwarn("Cannot detach the existing USB driver. Claiming the interface may fail. %s\n",
 			libusb_error_name(ret));
 
-	ret = libusb_claim_interface(ch347_data->handle, spi_interface);
+	ret = libusb_claim_interface(ch347_data->handle, iface);
 	if (ret != 0) {
 		msg_perr("Failed to claim interface 2: '%s'\n", libusb_error_name(ret));
 		goto error_exit;