Blame SOURCES/0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch

3e8f86
From f38f09da98acc63966b65b72029b1f7f81166bef Mon Sep 17 00:00:00 2001
3e8f86
From: Chris Dickens <christopher.a.dickens@gmail.com>
3e8f86
Date: Mon, 8 Feb 2021 11:56:13 -0800
3e8f86
Subject: [PATCH 2/2] linux_usbfs: Gracefully handle buggy devices with a
3e8f86
 configuration 0
3e8f86
3e8f86
The USB spec states that a configuration value of 0 is reserved and is
3e8f86
used to indicate the device in not configured (e.g. is in the address
3e8f86
state). Unfortunately some devices do exist that violate this and use 0
3e8f86
as the bConfigurationValue of the configuration descriptor.
3e8f86
3e8f86
Improve how the Linux backend handles such non-conformant devices by
3e8f86
adding special handling around the configuration value 0. Most devices
3e8f86
will not require this special handling, but for those that do there is
3e8f86
no way to distinguish between the device being unconfigured and using
3e8f86
configuration 0.
3e8f86
3e8f86
Closes #850
3e8f86
3e8f86
Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
3e8f86
---
3e8f86
 libusb/os/linux_usbfs.c | 94 ++++++++++++++++++++++++++---------------
3e8f86
 libusb/version_nano.h   |  2 +-
3e8f86
 2 files changed, 60 insertions(+), 36 deletions(-)
3e8f86
3e8f86
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
3e8f86
index ebf8cfe..3a1894c 100644
3e8f86
--- a/libusb/os/linux_usbfs.c
3e8f86
+++ b/libusb/os/linux_usbfs.c
3e8f86
@@ -128,7 +128,7 @@ struct linux_device_priv {
3e8f86
 	void *descriptors;
3e8f86
 	size_t descriptors_len;
3e8f86
 	struct config_descriptor *config_descriptors;
3e8f86
-	uint8_t active_config; /* cache val for !sysfs_available  */
3e8f86
+	int active_config; /* cache val for !sysfs_available  */
3e8f86
 };
3e8f86
 
3e8f86
 struct linux_device_handle_priv {
3e8f86
@@ -169,6 +169,21 @@ struct linux_transfer_priv {
3e8f86
 	int iso_packet_offset;
3e8f86
 };
3e8f86
 
3e8f86
+static int dev_has_config0(struct libusb_device *dev)
3e8f86
+{
3e8f86
+	struct linux_device_priv *priv = usbi_get_device_priv(dev);
3e8f86
+	struct config_descriptor *config;
3e8f86
+	uint8_t idx;
3e8f86
+
3e8f86
+	for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) {
3e8f86
+		config = &priv->config_descriptors[idx];
3e8f86
+		if (config->desc->bConfigurationValue == 0)
3e8f86
+			return 1;
3e8f86
+	}
3e8f86
+
3e8f86
+	return 0;
3e8f86
+}
3e8f86
+
3e8f86
 static int get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
3e8f86
 {
3e8f86
 	struct libusb_context *ctx = DEVICE_CTX(dev);
3e8f86
@@ -574,22 +589,12 @@ static int sysfs_scan_device(struct libusb_context *ctx, const char *devname)
3e8f86
 }
3e8f86
 
3e8f86
 /* read the bConfigurationValue for a device */
3e8f86
-static int sysfs_get_active_config(struct libusb_device *dev, uint8_t *config)
3e8f86
+static int sysfs_get_active_config(struct libusb_device *dev, int *config)
3e8f86
 {
3e8f86
 	struct linux_device_priv *priv = usbi_get_device_priv(dev);
3e8f86
-	int ret, tmp;
3e8f86
-
3e8f86
-	ret = read_sysfs_attr(DEVICE_CTX(dev), priv->sysfs_dir, "bConfigurationValue",
3e8f86
-			      UINT8_MAX, &tmp);
3e8f86
-	if (ret < 0)
3e8f86
-		return ret;
3e8f86
 
3e8f86
-	if (tmp == -1)
3e8f86
-		tmp = 0;	/* unconfigured */
3e8f86
-
3e8f86
-	*config = (uint8_t)tmp;
3e8f86
-
3e8f86
-	return 0;
3e8f86
+	return read_sysfs_attr(DEVICE_CTX(dev), priv->sysfs_dir, "bConfigurationValue",
3e8f86
+			UINT8_MAX, config);
3e8f86
 }
3e8f86
 
3e8f86
 int linux_get_device_address(struct libusb_context *ctx, int detached,
3e8f86
@@ -765,6 +770,9 @@ static int parse_config_descriptors(struct libusb_device *dev)
3e8f86
 			}
3e8f86
 		}
3e8f86
 
3e8f86
+		if (config_desc->bConfigurationValue == 0)
3e8f86
+			usbi_warn(ctx, "device has configuration 0");
3e8f86
+
3e8f86
 		priv->config_descriptors[idx].desc = config_desc;
3e8f86
 		priv->config_descriptors[idx].actual_len = config_len;
3e8f86
 
3e8f86
@@ -798,7 +806,7 @@ static int op_get_active_config_descriptor(struct libusb_device *dev,
3e8f86
 {
3e8f86
 	struct linux_device_priv *priv = usbi_get_device_priv(dev);
3e8f86
 	void *config_desc;
3e8f86
-	uint8_t active_config;
3e8f86
+	int active_config;
3e8f86
 	int r;
3e8f86
 
3e8f86
 	if (priv->sysfs_dir) {
3e8f86
@@ -810,12 +818,12 @@ static int op_get_active_config_descriptor(struct libusb_device *dev,
3e8f86
 		active_config = priv->active_config;
3e8f86
 	}
3e8f86
 
3e8f86
-	if (active_config == 0) {
3e8f86
+	if (active_config == -1) {
3e8f86
 		usbi_err(DEVICE_CTX(dev), "device unconfigured");
3e8f86
 		return LIBUSB_ERROR_NOT_FOUND;
3e8f86
 	}
3e8f86
 
3e8f86
-	r = op_get_config_descriptor_by_value(dev, active_config, &config_desc);
3e8f86
+	r = op_get_config_descriptor_by_value(dev, (uint8_t)active_config, &config_desc);
3e8f86
 	if (r < 0)
3e8f86
 		return r;
3e8f86
 
3e8f86
@@ -863,17 +871,26 @@ static int usbfs_get_active_config(struct libusb_device *dev, int fd)
3e8f86
 
3e8f86
 		/* we hit this error path frequently with buggy devices :( */
3e8f86
 		usbi_warn(DEVICE_CTX(dev), "get configuration failed, errno=%d", errno);
3e8f86
+
3e8f86
+		/* assume the current configuration is the first one if we have
3e8f86
+		 * the configuration descriptors, otherwise treat the device
3e8f86
+		 * as unconfigured. */
3e8f86
+		if (priv->config_descriptors)
3e8f86
+			priv->active_config = (int)priv->config_descriptors[0].desc->bConfigurationValue;
3e8f86
+		else
3e8f86
+			priv->active_config = -1;
3e8f86
 	} else if (active_config == 0) {
3e8f86
-		/* some buggy devices have a configuration 0, but we're
3e8f86
-		 * reaching into the corner of a corner case here, so let's
3e8f86
-		 * not support buggy devices in these circumstances.
3e8f86
-		 * stick to the specs: a configuration value of 0 means
3e8f86
-		 * unconfigured. */
3e8f86
-		usbi_warn(DEVICE_CTX(dev), "active cfg 0? assuming unconfigured device");
3e8f86
+		if (dev_has_config0(dev)) {
3e8f86
+			/* some buggy devices have a configuration 0, but we're
3e8f86
+			 * reaching into the corner of a corner case here. */
3e8f86
+			priv->active_config = 0;
3e8f86
+		} else {
3e8f86
+			priv->active_config = -1;
3e8f86
+		}
3e8f86
+	} else {
3e8f86
+		priv->active_config = (int)active_config;
3e8f86
 	}
3e8f86
 
3e8f86
-	priv->active_config = active_config;
3e8f86
-
3e8f86
 	return LIBUSB_SUCCESS;
3e8f86
 }
3e8f86
 
3e8f86
@@ -1004,9 +1021,9 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
3e8f86
 		usbi_warn(ctx, "Missing rw usbfs access; cannot determine "
3e8f86
 			       "active configuration descriptor");
3e8f86
 		if (priv->config_descriptors)
3e8f86
-			priv->active_config = priv->config_descriptors[0].desc->bConfigurationValue;
3e8f86
+			priv->active_config = (int)priv->config_descriptors[0].desc->bConfigurationValue;
3e8f86
 		else
3e8f86
-			priv->active_config = 0; /* No config dt */
3e8f86
+			priv->active_config = -1; /* No config dt */
3e8f86
 
3e8f86
 		return LIBUSB_SUCCESS;
3e8f86
 	}
3e8f86
@@ -1428,22 +1445,27 @@ static int op_get_configuration(struct libusb_device_handle *handle,
3e8f86
 	uint8_t *config)
3e8f86
 {
3e8f86
 	struct linux_device_priv *priv = usbi_get_device_priv(handle->dev);
3e8f86
+	int active_config;
3e8f86
 	int r;
3e8f86
 
3e8f86
 	if (priv->sysfs_dir) {
3e8f86
-		r = sysfs_get_active_config(handle->dev, config);
3e8f86
+		r = sysfs_get_active_config(handle->dev, &active_config);
3e8f86
 	} else {
3e8f86
 		struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle);
3e8f86
 
3e8f86
 		r = usbfs_get_active_config(handle->dev, hpriv->fd);
3e8f86
 		if (r == LIBUSB_SUCCESS)
3e8f86
-			*config = priv->active_config;
3e8f86
+			active_config = priv->active_config;
3e8f86
 	}
3e8f86
 	if (r < 0)
3e8f86
 		return r;
3e8f86
 
3e8f86
-	if (*config == 0)
3e8f86
-		usbi_err(HANDLE_CTX(handle), "device unconfigured");
3e8f86
+	if (active_config == -1) {
3e8f86
+		usbi_warn(HANDLE_CTX(handle), "device unconfigured");
3e8f86
+		active_config = 0;
3e8f86
+	}
3e8f86
+
3e8f86
+	*config = (uint8_t)active_config;
3e8f86
 
3e8f86
 	return 0;
3e8f86
 }
3e8f86
@@ -1467,11 +1489,13 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config)
3e8f86
 		return LIBUSB_ERROR_OTHER;
3e8f86
 	}
3e8f86
 
3e8f86
-	if (config == -1)
3e8f86
-		config = 0;
3e8f86
+	/* if necessary, update our cached active config descriptor */
3e8f86
+	if (!priv->sysfs_dir) {
3e8f86
+		if (config == 0 && !dev_has_config0(handle->dev))
3e8f86
+			config = -1;
3e8f86
 
3e8f86
-	/* update our cached active config descriptor */
3e8f86
-	priv->active_config = (uint8_t)config;
3e8f86
+		priv->active_config = config;
3e8f86
+	}
3e8f86
 
3e8f86
 	return LIBUSB_SUCCESS;
3e8f86
 }
3e8f86
-- 
3e8f86
2.29.2
3e8f86