Commit 166147a80387f75160d97a8f1166c6f3f6e6829d

Kano 2013-09-19T21:11:40

ubsutils interfaces much each have their own handle

diff --git a/usbutils.c b/usbutils.c
index af35d4e..0a28925 100644
--- a/usbutils.c
+++ b/usbutils.c
@@ -1379,7 +1379,8 @@ static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb)
 	if (cgusb->prod_string && cgusb->prod_string != BLANK)
 		free(cgusb->prod_string);
 
-	free(cgusb->descriptor);
+	if (cgusb->descriptor)
+		free(cgusb->descriptor);
 
 	free(cgusb->found);
 
@@ -1391,7 +1392,7 @@ static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb)
 	return NULL;
 }
 
-void _usb_uninit(struct cgpu_info *cgpu)
+static void _usb_uninit(struct cgpu_info *cgpu)
 {
 	applog(LOG_DEBUG, "USB uninit %s%i",
 			cgpu->drv->name, cgpu->device_id);
@@ -1400,18 +1401,12 @@ void _usb_uninit(struct cgpu_info *cgpu)
 	//  if release_cgpu() was called due to a USB NODEV(err)
 	if (!cgpu->usbdev)
 		return;
+
 	if (cgpu->usbdev->handle) {
-		int ifinfo = 0;
-		while (cgpu->usbdev->claimed > 0) {
-			libusb_release_interface(cgpu->usbdev->handle,
-						 cgpu->usbdev->found->intinfos[ifinfo].interface);
-			ifinfo++;
-			cgpu->usbdev->claimed--;
-		}
+		libusb_release_interface(cgpu->usbdev->handle, USBIF(cgpu->usbdev));
 		cg_wlock(&cgusb_fd_lock);
 		libusb_close(cgpu->usbdev->handle);
 		cgpu->usbdev->handle = NULL;
-		cgpu->usbdev->claimed = 0;
 		cg_wunlock(&cgusb_fd_lock);
 	}
 	cgpu->usbdev = free_cgusb(cgpu->usbdev);
@@ -1474,11 +1469,16 @@ static void release_cgpu(struct cgpu_info *cgpu)
 	cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address);
 }
 
-// Currently only used by MMQ
+// Used by MMQ - use the same usbdev thus locking is across all 4 related devices
+// since they must use the same USB handle since they use the same interface
 struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig)
 {
-	struct cgpu_info *copy = calloc(1, sizeof(*copy));
+	struct cgpu_info *copy;
+	int pstate;
 
+	DEVLOCK(orig, pstate);
+
+	copy = calloc(1, sizeof(*copy));
 	if (unlikely(!copy))
 		quit(1, "Failed to calloc cgpu for %s in usb_copy_cgpu", orig->drv->dname);
 
@@ -1495,6 +1495,70 @@ struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig)
 
 	copy->usbinfo.devlock = orig->usbinfo.devlock;
 
+	DEVUNLOCK(orig, pstate);
+
+	return copy;
+}
+
+// Used by CMR - use a different usbdev - since they must use a different
+// USB handle due to using different interfaces (libusb requirement)
+static struct cgpu_info *usb_dup_cgpu(struct cgpu_info *orig, int intinfo)
+{
+	struct cgpu_info *copy;
+
+	copy = calloc(1, sizeof(*copy));
+	if (unlikely(!copy))
+		quit(1, "Failed to calloc cgpu for %s in usb_dup_cgpu", orig->drv->dname);
+
+	copy->name = orig->name;
+	copy->drv = copy_drv(orig->drv);
+	copy->deven = orig->deven;
+	copy->threads = orig->threads;
+
+	if (orig->usbdev) {
+		copy->usbdev = calloc(1, sizeof(*(copy->usbdev)));
+		if (unlikely(!copy->usbdev))
+			quit(1, "Failed to calloc usbdev for %s in usb_dup_cgpu", orig->drv->dname);
+
+		copy->usbdev->found = malloc(sizeof(*(copy->usbdev->found)));
+		if (unlikely(!copy->usbdev->found))
+			quit(1, "Failed to malloc found for %s in usb_dup_cgpu", orig->drv->dname);
+		memcpy(copy->usbdev->found, orig->usbdev->found, sizeof(*(copy->usbdev->found)));
+
+		copy->usbdev->found->which_intinfo = intinfo;
+
+		copy->usbdev->descriptor = NULL; // don't need it
+		copy->usbdev->usb_type = orig->usbdev->usb_type;
+		copy->usbdev->ident = orig->usbdev->ident;
+		copy->usbdev->usbver = orig->usbdev->usbver;
+		copy->usbdev->cps = orig->usbdev->cps;
+		copy->usbdev->usecps = orig->usbdev->usecps;
+		if (orig->usbdev->prod_string == BLANK)
+			copy->usbdev->prod_string = (char *)BLANK;
+		else
+			copy->usbdev->prod_string = strdup(orig->usbdev->prod_string);
+		if (orig->usbdev->manuf_string == BLANK)
+			copy->usbdev->manuf_string = (char *)BLANK;
+		else
+			copy->usbdev->manuf_string = strdup(orig->usbdev->manuf_string);
+		if (orig->usbdev->serial_string == BLANK)
+			copy->usbdev->serial_string = (char *)BLANK;
+		else
+			copy->usbdev->serial_string = strdup(orig->usbdev->serial_string);
+		copy->usbdev->fwVersion = orig->usbdev->fwVersion;
+		copy->usbdev->interfaceVersion = orig->usbdev->interfaceVersion;
+	}
+
+	memcpy(&(copy->usbinfo), &(orig->usbinfo), sizeof(copy->usbinfo));
+
+	copy->usbinfo.nodev = (copy->usbdev == NULL);
+
+	copy->usbinfo.devlock = calloc(1, sizeof(*(copy->usbinfo.devlock)));
+	if (unlikely(!copy->usbinfo.devlock))
+		quit(1, "Failed to calloc devlock for %s in usb_dup_cgpu", orig->drv->dname);
+
+	rwlock_init(copy->usbinfo.devlock);
+
 	return copy;
 }
 
@@ -1616,22 +1680,18 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u
 	}
 
 #ifndef WIN32
-	for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) {
-		int interface = found->intinfos[ifinfo].interface;
-
-		if (libusb_kernel_driver_active(cgusb->handle, interface) == 1) {
-			applog(LOG_DEBUG, "USB init, kernel attached ... %s", devstr);
-			err = libusb_detach_kernel_driver(cgusb->handle, interface);
-			if (err == 0) {
-				applog(LOG_DEBUG,
-					"USB init, kernel detached interface %d successfully %s",
-					interface, devstr);
-			} else {
-				applog(LOG_WARNING,
-					"USB init, kernel detach interface %d failed, err %d in use? %s",
-					interface, err, devstr);
-				goto cldame;
-			}
+	if (libusb_kernel_driver_active(cgusb->handle, FOUNDIF(found)) == 1) {
+		applog(LOG_DEBUG, "USB init, kernel attached ... %s", devstr);
+		err = libusb_detach_kernel_driver(cgusb->handle, FOUNDIF(found));
+		if (err == 0) {
+			applog(LOG_DEBUG,
+				"USB init, kernel detached interface %d successfully %s",
+				FOUNDIF(found), devstr);
+		} else {
+			applog(LOG_WARNING,
+				"USB init, kernel detach interface %d failed, err %d in use? %s",
+				FOUNDIF(found), err, devstr);
+			goto cldame;
 		}
 	}
 #endif
@@ -1754,25 +1814,20 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u
 				goto cldame;
 			}
 
-	cgusb->claimed = 0;
-	for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) {
-		int interface = found->intinfos[ifinfo].interface;
-		err = libusb_claim_interface(cgusb->handle, interface);
-		if (err) {
-			switch(err) {
-				case LIBUSB_ERROR_BUSY:
-					applog(LOG_WARNING,
-						"USB init, claim interface %d in use %s",
-						interface, devstr);
-					break;
-				default:
-					applog(LOG_DEBUG,
-						"USB init, claim interface %d failed, err %d %s",
-						interface, err, devstr);
-			}
-			goto reldame;
+	err = libusb_claim_interface(cgusb->handle, FOUNDIF(found));
+	if (err) {
+		switch(err) {
+			case LIBUSB_ERROR_BUSY:
+				applog(LOG_WARNING,
+					"USB init, claim interface %d in use %s",
+					FOUNDIF(found), devstr);
+				break;
+			default:
+				applog(LOG_DEBUG,
+					"USB init, claim interface %d failed, err %d %s",
+					FOUNDIF(found), err, devstr);
 		}
-		cgusb->claimed++;
+		goto reldame;
 	}
 
 	cfg = -1;
@@ -1839,19 +1894,13 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u
 
 reldame:
 
-	ifinfo = 0;
-	while (cgusb->claimed > 0) {
-		libusb_release_interface(cgusb->handle, found->intinfos[ifinfo].interface);
-		ifinfo++;
-		cgusb->claimed--;
-	}
+	libusb_release_interface(cgusb->handle, FOUNDIF(found));
 
 cldame:
 
 	cg_wlock(&cgusb_fd_lock);
 	libusb_close(cgusb->handle);
 	cgusb->handle = NULL;
-	cgusb->claimed = 0;
 	cg_wunlock(&cgusb_fd_lock);
 
 dame:
@@ -1867,6 +1916,144 @@ out_unlock:
 	return bad;
 }
 
+// To get the extra interfaces on a multi interface device
+struct cgpu_info *usb_init_intinfo(struct cgpu_info *orig, int intinfo)
+{
+	struct usb_find_devices *found;
+	struct libusb_device *dev;
+	struct cgpu_info *copy = NULL;
+	char msgstr[STRBUFLEN+1];
+	char devstr[STRBUFLEN+1];
+	char devpath[32];
+	int err, pstate;
+
+	DEVLOCK(orig, pstate);
+
+	snprintf(msgstr, sizeof(msgstr), "USB %s init_intinfo (%d:%d:ii%d)",
+			orig->drv->dname,
+			(int)(orig->usbinfo.bus_number),
+			(int)(orig->usbinfo.device_address),
+			orig->usbdev->found->which_intinfo);
+
+	if (orig->usbinfo.nodev) {
+		applog(LOG_ERR, "%s cgpu has nodev", msgstr);
+		goto Hitagi;
+	}
+
+	if (orig->usbdev->found->which_intinfo != 0) {
+		applog(LOG_ERR, "%s incorrect cgpu (must be ii0)", msgstr);
+		goto Hitagi;
+	}
+
+	if (orig->usbdev->found->intinfo_count < 2) {
+		applog(LOG_ERR, "%s cgpu only has 1 interface", msgstr);
+		goto Hitagi;
+	}
+
+	if (intinfo < 1 || intinfo >= orig->usbdev->found->intinfo_count) {
+		applog(LOG_ERR, "%s invalid intinfo (%d) must be > 0 && < %d",
+				msgstr, intinfo, orig->usbdev->found->intinfo_count);
+		goto Hitagi;
+	}
+
+	dev = libusb_get_device(orig->usbdev->handle);
+
+	copy = usb_dup_cgpu(orig, intinfo);
+	if (!copy)
+		goto Hitagi;
+
+	found = copy->usbdev->found;
+
+	snprintf(devpath, sizeof(devpath), "%d:%d:%d",
+		(int)(copy->usbinfo.bus_number),
+		(int)(copy->usbinfo.device_address),
+		intinfo);
+
+	copy->device_path = strdup(devpath);
+
+	snprintf(devstr, sizeof(devstr), "- %s device %s", found->name, devpath);
+
+	cg_wlock(&cgusb_fd_lock);
+	err = libusb_open(dev, &(copy->usbdev->handle));
+	cg_wunlock(&cgusb_fd_lock);
+	if (err) {
+		switch (err) {
+			case LIBUSB_ERROR_ACCESS:
+				applog(LOG_ERR,
+					"USB init_intinfo, open device failed, err %d, "
+					"you don't have privilege to access %s",
+					err, devstr);
+				break;
+#ifdef WIN32
+			// Windows specific message
+			case LIBUSB_ERROR_NOT_SUPPORTED:
+				applog(LOG_ERR,
+					"USB init_intinfo, open device failed, err %d, "
+					"you need to install a WinUSB driver for %s",
+					err, devstr);
+				break;
+#endif
+			default:
+				applog(LOG_DEBUG,
+					"USB init_intinfo, open failed, err %d %s",
+					err, devstr);
+		}
+		goto Hitagi;
+	}
+
+#ifndef WIN32
+	if (libusb_kernel_driver_active(copy->usbdev->handle, FOUNDIF(found)) == 1) {
+		applog(LOG_DEBUG, "USB init_intinfo, kernel attached ... %s", devstr);
+		err = libusb_detach_kernel_driver(copy->usbdev->handle, FOUNDIF(found));
+		if (err == 0) {
+			applog(LOG_DEBUG,
+				"USB init_intinfo, kernel detached interface %d successfully %s",
+				FOUNDIF(found), devstr);
+		} else {
+			applog(LOG_WARNING,
+				"USB init_intinfo, kernel detach interface %d failed, err %d in use? %s",
+				FOUNDIF(found), err, devstr);
+			goto HitagiClose;
+		}
+	}
+#endif
+
+	err = libusb_claim_interface(copy->usbdev->handle, FOUNDIF(found));
+	if (err) {
+		switch(err) {
+			case LIBUSB_ERROR_BUSY:
+				applog(LOG_WARNING,
+					"USB init_intinfo, claim interface %d in use %s",
+					FOUNDIF(found), devstr);
+				break;
+			default:
+				applog(LOG_DEBUG,
+					"USB init_intinfo, claim interface %d failed, err %d %s",
+					FOUNDIF(found), err, devstr);
+		}
+		goto HitagiClose;
+	}
+
+	goto Hitagi;
+
+HitagiClose:
+
+	cg_wlock(&cgusb_fd_lock);
+	libusb_close(copy->usbdev->handle);
+	copy->usbdev->handle = NULL;
+	cg_wunlock(&cgusb_fd_lock);
+
+	copy->usbdev = free_cgusb(copy->usbdev);
+
+	copy = usb_free_cgpu(copy);
+
+Hitagi:
+
+	DEVUNLOCK(orig, pstate);
+
+	return copy;
+}
+
 bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found_match)
 {
 	struct usb_find_devices *found_use = NULL;
diff --git a/usbutils.h b/usbutils.h
index a68f899..0abb05d 100644
--- a/usbutils.h
+++ b/usbutils.h
@@ -177,7 +177,6 @@ enum usb_types {
 struct cg_usb_device {
 	struct usb_find_devices *found;
 	libusb_device_handle *handle;
-	int claimed;
 	pthread_mutex_t *mutex;
 	struct libusb_device_descriptor *descriptor;
 	enum usb_types usb_type;
@@ -336,6 +335,7 @@ struct cgpu_info *usb_alloc_cgpu(struct device_drv *drv, int threads);
 struct cgpu_info *usb_free_cgpu_devlock(struct cgpu_info *cgpu, bool free_devlock);
 #define usb_free_cgpu(cgpu) usb_free_cgpu_devlock(cgpu, true)
 void usb_uninit(struct cgpu_info *cgpu);
+struct cgpu_info *usb_init_intinfo(struct cgpu_info *orig,  int intinfo);
 bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found);
 void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *));
 struct api_data *api_usb_stats(int *count);