From bad99299204aa239c900cf9c9723003a5c1802b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kukri?= <kukri.mate@gmail.com>
Date: Wed, 26 Dec 2018 11:32:59 +0100
Subject: [PATCH 1/5] Working with libusb.

Change-Id: I386f28264d25f319f9ca4e8d0812f80127072877
---
 Makefile                |   5 +-
 libmcp2210/hid_libusb.c | 174 ++++++++++++++++++++++++++++++++++++++++
 libmcp2210/hid_linux.c  | 131 ------------------------------
 3 files changed, 177 insertions(+), 133 deletions(-)
 create mode 100644 libmcp2210/hid_libusb.c
 delete mode 100644 libmcp2210/hid_linux.c

diff --git a/Makefile b/Makefile
index 48cf96d..8d47c7b 100644
--- a/Makefile
+++ b/Makefile
@@ -712,6 +712,7 @@ override CONFIG_CH341A_SPI = no
 override CONFIG_DEDIPROG = no
 override CONFIG_DIGILENT_SPI = no
 override CONFIG_DEVELOPERBOX_SPI = no
+override CONFIG_MCP2210_SPI = no
 endif
 ifeq ($(CONFIG_ENABLE_LIBPCI_PROGRAMMERS), no)
 override CONFIG_INTERNAL = no
@@ -993,9 +994,9 @@ endif
 
 ifeq ($(CONFIG_MCP2210_SPI), yes)
 FEATURE_CFLAGS += -D'CONFIG_MCP2210_SPI=1'
-PROGRAMMER_OBJS += libmcp2210/hid_linux.o libmcp2210/libmcp2210.o \
+PROGRAMMER_OBJS += libmcp2210/hid_libusb.o libmcp2210/libmcp2210.o \
 	mcp2210_spi.o
-LIBS += -ludev
+NEED_LIBUSB1 += CONFIG_MCP2210_SPI
 endif
 
 ifneq ($(NEED_SERIAL), )
diff --git a/libmcp2210/hid_libusb.c b/libmcp2210/hid_libusb.c
new file mode 100644
index 0000000..cb6dc2d
--- /dev/null
+++ b/libmcp2210/hid_libusb.c
@@ -0,0 +1,174 @@
+/* HID support code for libusb */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libusb.h>
+#include "hid.h"
+
+// HID handle
+struct hid_handle {
+	// libusb handle
+	libusb_device_handle *libusb_handle;
+
+	// IN and OUT endpoints
+	uint8_t endpoint_in, endpoint_out;
+
+	// User-friendly text description
+	char text[33];
+};
+
+int hid_init()
+{
+	if (libusb_init(NULL) < 0) {
+		return -1;
+	}
+	return 0;
+}
+
+ssize_t hid_find_devices(uint16_t vid, uint16_t pid, hid_handle_t **dest, size_t dest_len)
+{
+	libusb_device **devices;
+	ssize_t device_count = libusb_get_device_list(NULL, &devices);
+	if (device_count < 0)
+		return -1;
+
+	size_t dev_index = 0;
+
+	for (size_t dev_i = 0; dev_i < device_count; ++dev_i) {
+		struct libusb_device_descriptor dev_desc;
+
+		if (libusb_get_device_descriptor(devices[dev_i], &dev_desc) < 0) {
+			goto err;
+		}
+
+		if (dev_desc.idVendor == vid && dev_desc.idProduct == pid) {
+			// Create a handle and open the device
+			hid_handle_t *handle = malloc(sizeof(hid_handle_t));
+			if (libusb_open(devices[dev_i], &handle->libusb_handle) < 0) {
+				free(handle);
+				goto err;
+			}
+
+			// Now check if it actually has a HID interface and claim it
+			struct libusb_config_descriptor *conf_desc;
+			if (libusb_get_config_descriptor(devices[dev_i], 0, &conf_desc) < 0) {
+				libusb_close(handle->libusb_handle);
+				free(handle);
+				goto err;
+			}
+
+			// FIXME: This works with the MCP2210, but someone more familiar with
+			// libusb and USB terminology should clean this up
+			if (conf_desc->bNumInterfaces != 1 ||
+					conf_desc->interface[0].num_altsetting != 1 ||
+					conf_desc->interface[0].altsetting[0].bInterfaceClass
+						!= LIBUSB_CLASS_HID) {
+				libusb_free_config_descriptor(conf_desc);
+				libusb_close(handle->libusb_handle);
+				free(handle);
+				goto err;
+			}
+
+			size_t in_cnt = 0, out_cnt = 0;
+
+			// We need an IN and OUT interrupt endpoint
+			for (size_t ep_i = 0; ep_i < conf_desc->interface[0]
+					.altsetting[0].bNumEndpoints; ++ep_i) {
+				const struct libusb_endpoint_descriptor *endpoint =
+					&conf_desc->interface[0].altsetting[0].endpoint[ep_i];
+
+				// Looking only for interrupt transfer endpoints
+				if ((endpoint->bmAttributes | LIBUSB_TRANSFER_TYPE_MASK) |
+						LIBUSB_TRANSFER_TYPE_INTERRUPT) {
+					if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
+						handle->endpoint_in = endpoint->bEndpointAddress;
+						in_cnt++;
+					} else {
+						handle->endpoint_out = endpoint->bEndpointAddress;
+						out_cnt++;
+					}
+				}
+			}
+
+			// It should only have one IN and one OUT endpoint
+			if (in_cnt != 1 || out_cnt != 1) {
+				libusb_free_config_descriptor(conf_desc);
+				libusb_close(handle->libusb_handle);
+				free(handle);
+				goto err;
+			}
+
+			libusb_set_auto_detach_kernel_driver(handle->libusb_handle, 1);
+			if (libusb_claim_interface(handle->libusb_handle, 0) < 0) {
+				libusb_free_config_descriptor(conf_desc);
+				libusb_close(handle->libusb_handle);
+				free(handle);
+				goto err;
+			}
+
+			// Generate a user friendly description of the device
+			snprintf(handle->text, 33, "MCP2210 => VID: %04x, PID: %04x\n",
+				dev_desc.idVendor, dev_desc.idProduct);
+
+			// Add it to the array
+			dest[dev_index++] = handle;
+
+			// Cleanup
+			libusb_free_config_descriptor(conf_desc);
+		}
+	}
+
+	libusb_free_device_list(devices, 1);
+	return dev_index;
+err:
+	libusb_free_device_list(devices, 1);
+	return -1;
+}
+
+const char *hid_device_desc(hid_handle_t *handle)
+{
+	return handle->text;
+}
+
+ssize_t hid_write(hid_handle_t *handle, void *data, size_t data_len)
+{
+	int transferred;
+	if (libusb_interrupt_transfer(handle->libusb_handle, handle->endpoint_out,
+			data, data_len, &transferred, 0) < 0)
+		return -1;
+	if (transferred != data_len) {
+		errno = EINVAL;
+		return -1;
+	}
+	return 0;
+}
+
+ssize_t hid_read(hid_handle_t *handle, void *buffer, size_t buffer_len)
+{
+	int read;
+	if (libusb_interrupt_transfer(handle->libusb_handle, handle->endpoint_in,
+			buffer, buffer_len, &read, 0) < 0)
+		return -1;
+	if (read != buffer_len) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	return 0;
+}
+
+void hid_cleanup_device(hid_handle_t *handle)
+{
+	libusb_release_interface(handle->libusb_handle, 0);
+	libusb_close(handle->libusb_handle);
+	free(handle);
+}
+
+void hid_fini()
+{
+	libusb_exit(NULL);
+}
diff --git a/libmcp2210/hid_linux.c b/libmcp2210/hid_linux.c
deleted file mode 100644
index 94c91f6..0000000
--- a/libmcp2210/hid_linux.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/* HID support code on Linux */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <libudev.h>
-#include "hid.h"
-
-// HID handle
-struct hid_handle {
-	char *devpath;
-	int fd;
-};
-
-// udev handle
-static struct udev* udev = NULL;
-
-int hid_init()
-{
-	if (!udev) {
-		if (!(udev = udev_new()))
-			return -1;
-	}
-	return 0;
-}
-
-ssize_t hid_find_devices(uint16_t vid, uint16_t pid, hid_handle_t **dest, size_t dest_len)
-{
-	if (!udev) {
-		errno = EINVAL;
-		return -1;
-	}
-
-	if (!dest_len) {
-		errno = ENOMEM;
-		return -1;
-	}
-
-	struct udev_enumerate *enumerate = udev_enumerate_new(udev);
-	if (!enumerate)
-		goto err1;
-
-	udev_enumerate_add_match_subsystem(enumerate, "hidraw");
-	udev_enumerate_scan_devices(enumerate);
-
-	struct udev_list_entry *cur, *all = udev_enumerate_get_list_entry(enumerate);
-	if (!all)
-		goto err2;
-
-	size_t i = 0;
-	udev_list_entry_foreach(cur, all) {
-		struct udev_device *device =
-		udev_device_new_from_syspath(udev, udev_list_entry_get_name(cur));
-		if (!device)
-			goto err2;
-
-		struct udev_device *parent =
-		udev_device_get_parent_with_subsystem_devtype(
-			device, "usb", "usb_device");
-
-		if (!parent) // Ignore non-usb HID devices
-			goto loop_cleanup;
-
-		uint16_t cur_vid = (uint16_t) strtol(udev_device_get_sysattr_value(parent, "idVendor"), NULL, 16);
-		uint16_t cur_pid = (uint16_t) strtol(udev_device_get_sysattr_value(parent, "idProduct"), NULL, 16);
-		if (cur_vid == vid && cur_pid == pid) {
-			struct hid_handle *handle = malloc(sizeof(struct hid_handle));
-			handle->devpath = strdup(udev_device_get_devnode(device));
-			handle->fd = -1;
-			dest[i++] = handle;
-		}
-
-		loop_cleanup:
-		udev_device_unref(device);
-		if (i >= dest_len) {
-			errno = ENOMEM;
-			goto err2;
-		}
-	}
-
-	udev_enumerate_unref(enumerate);
-	return (ssize_t) i;
-	err2:
-	udev_enumerate_unref(enumerate);
-	err1:
-	return -1;
-}
-
-const char *hid_device_desc(hid_handle_t *handle)
-{
-	return handle->devpath;
-}
-
-ssize_t hid_write(struct hid_handle *handle, void *data, size_t data_len)
-{
-	if (handle->fd == -1) {
-		handle->fd = open(handle->devpath, O_RDWR);
-		if (handle->fd == -1)
-			return -1;
-	}
-	return write(handle->fd, data, data_len);
-}
-
-ssize_t hid_read(struct hid_handle *handle, void *buffer, size_t buffer_len)
-{
-	if (handle->fd == -1) {
-		handle->fd = open(handle->devpath, O_RDWR);
-		if (handle->fd == -1)
-			return -1;
-	}
-	return read(handle->fd, buffer, buffer_len);
-}
-
-void hid_cleanup_device(struct hid_handle *handle)
-{
-	if (handle->fd != -1)
-		close(handle->fd);
-	free(handle->devpath);
-	free(handle);
-}
-
-void hid_fini()
-{
-	if (udev) {
-		udev_unref(udev);
-		udev = NULL;
-	}
-}
-- 
2.20.1

