X-Git-Url: https://git.ao2.it/libam7xxx.git/blobdiff_plain/313d4a702aefeac3c8360141ae9037a8fdd47746..9e24b798ba1376655756c55fd157661148799422:/src/am7xxx.c

diff --git a/src/am7xxx.c b/src/am7xxx.c
index 321a193..94b01ac 100644
--- a/src/am7xxx.c
+++ b/src/am7xxx.c
@@ -34,7 +34,7 @@
  * taken from: http://unixwiz.net/techtips/gnu-c-attributes.html)
  */
 #ifndef __GNUC__
-#  define  __attribute__(x)  /*NOTHING*/
+#  define  __attribute__(x)  /* NOTHING */
 #endif
 
 /* Control shared library symbols visibility */
@@ -385,8 +385,8 @@ static int read_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
 
 	ret = libusb_bulk_transfer(dev->usb_device, 0x81, buffer, len, &transferred, 0);
 	if (ret != 0 || (unsigned int)transferred != len) {
-		error(dev->ctx, "ret: %d\ttransferred: %d (expected %u)\n",
-		      ret, transferred, len);
+		error(dev->ctx, "%s. Transferred: %d (expected %u)\n",
+		      libusb_error_name(ret), transferred, len);
 		return ret;
 	}
 
@@ -404,8 +404,8 @@ static int send_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
 
 	ret = libusb_bulk_transfer(dev->usb_device, 0x1, buffer, len, &transferred, 0);
 	if (ret != 0 || (unsigned int)transferred != len) {
-		error(dev->ctx, "ret: %d\ttransferred: %d (expected %u)\n",
-		      ret, transferred, len);
+		error(dev->ctx, "%s. Transferred: %d (expected %u)\n",
+		      libusb_error_name(ret), transferred, len);
 		return ret;
 	}
 
@@ -489,8 +489,8 @@ static int send_data_async(am7xxx_device *dev, uint8_t *buffer, unsigned int len
 
 	/* Make a copy of the buffer so the caller can safely reuse it just
 	 * after libusb_submit_transfer() has returned. This technique
-	 * requires more allocations than a proper double-buffering approach
-	 * but it takes a lot less code. */
+	 * requires more dynamic allocations compared to a proper
+	 * double-buffering approach but it takes a lot less code. */
 	transfer_buffer = malloc(len);
 	if (transfer_buffer == NULL) {
 		error(dev->ctx, "cannot allocate transfer buffer (%s)\n",
@@ -704,7 +704,7 @@ static am7xxx_device *find_device(am7xxx_context *ctx,
 
 static int open_device(am7xxx_context *ctx,
 		       unsigned int device_index,
-		       libusb_device* usb_dev,
+		       libusb_device *usb_dev,
 		       am7xxx_device **dev)
 {
 	int ret;
@@ -725,7 +725,7 @@ static int open_device(am7xxx_context *ctx,
 
 	ret = libusb_open(usb_dev, &((*dev)->usb_device));
 	if (ret < 0) {
-		debug(ctx, "libusb_open failed\n");
+		debug(ctx, "libusb_open failed: %s\n", libusb_error_name(ret));
 		goto out;
 	}
 
@@ -734,22 +734,53 @@ static int open_device(am7xxx_context *ctx,
 	 */
 
 	current_configuration = -1;
-	libusb_get_configuration((*dev)->usb_device, &current_configuration);
+	ret = libusb_get_configuration((*dev)->usb_device,
+				       &current_configuration);
+	if (ret < 0) {
+		debug(ctx, "libusb_get_configuration failed: %s\n",
+		      libusb_error_name(ret));
+		goto out_libusb_close;
+	}
+
 	if (current_configuration != (*dev)->desc->configuration) {
+		/*
+		 * In principle, before setting a new configuration, kernel
+		 * drivers should be detached from _all_ interfaces; for
+		 * example calling something like the following "invented"
+		 * function _before_ setting the new configuration:
+		 *
+		 *   libusb_detach_all_kernel_drivers((*dev)->usb_device);
+		 *
+		 * However, in practice, this is not necessary for most
+		 * devices as they have only one configuration.
+		 *
+		 * When a device only has one configuration:
+		 *
+		 *   - if there was a kernel driver bound to the device, it
+		 *     had already set the configuration and the call below
+		 *     will be skipped;
+		 *
+		 *   - if no kernel driver was bound to the device, the call
+		 *     below will suceed.
+		 */
 		ret = libusb_set_configuration((*dev)->usb_device,
 					       (*dev)->desc->configuration);
 		if (ret < 0) {
-			debug(ctx, "libusb_set_configuration failed\n");
+			debug(ctx, "libusb_set_configuration failed: %s\n",
+			      libusb_error_name(ret));
 			debug(ctx, "Cannot set configuration %hhu\n",
 			      (*dev)->desc->configuration);
 			goto out_libusb_close;
 		}
 	}
 
+	libusb_set_auto_detach_kernel_driver((*dev)->usb_device, 1);
+
 	ret = libusb_claim_interface((*dev)->usb_device,
 				     (*dev)->desc->interface_number);
 	if (ret < 0) {
-		debug(ctx, "libusb_claim_interface failed\n");
+		debug(ctx, "libusb_claim_interface failed: %s\n",
+		      libusb_error_name(ret));
 		debug(ctx, "Cannot claim interface %hhu\n",
 		      (*dev)->desc->interface_number);
 		goto out_libusb_close;
@@ -759,16 +790,26 @@ static int open_device(am7xxx_context *ctx,
 	 * http://libusb.sourceforge.net/api-1.0/caveats.html
 	 */
 	current_configuration = -1;
-	libusb_get_configuration((*dev)->usb_device, &current_configuration);
+	ret = libusb_get_configuration((*dev)->usb_device,
+				       &current_configuration);
+	if (ret < 0) {
+		debug(ctx, "libusb_get_configuration after claim failed: %s\n",
+		      libusb_error_name(ret));
+		goto out_libusb_release_interface;
+	}
+
 	if (current_configuration != (*dev)->desc->configuration) {
-		debug(ctx, "libusb configuration changed\n");
-		debug(ctx, "Cannot claim interface %hhu\n",
-		      (*dev)->desc->interface_number);
-		goto out_libusb_close;
+		debug(ctx, "libusb configuration changed (expected: %hhu, current: %hhu\n",
+		      (*dev)->desc->configuration, current_configuration);
+		ret = -EINVAL;
+		goto out_libusb_release_interface;
 	}
 out:
 	return ret;
 
+out_libusb_release_interface:
+	libusb_release_interface((*dev)->usb_device,
+				 (*dev)->desc->interface_number);
 out_libusb_close:
 	libusb_close((*dev)->usb_device);
 	(*dev)->usb_device = NULL;
@@ -802,7 +843,7 @@ static int scan_devices(am7xxx_context *ctx, scan_op op,
 			unsigned int open_device_index, am7xxx_device **dev)
 {
 	ssize_t num_devices;
-	libusb_device** list;
+	libusb_device **list;
 	unsigned int current_index;
 	int i;
 	int ret;
@@ -1060,8 +1101,10 @@ AM7XXX_PUBLIC int am7xxx_init(am7xxx_context **ctx)
 	(*ctx)->log_level = AM7XXX_LOG_TRACE;
 
 	ret = libusb_init(&((*ctx)->usb_context));
-	if (ret < 0)
+	if (ret < 0) {
+		error(*ctx, "libusb_init failed: %s\n", libusb_error_name(ret));
 		goto out_free_context;
+	}
 
 	libusb_set_debug((*ctx)->usb_context, LIBUSB_LOG_LEVEL_INFO);