Browse Source

Merge pull request #441 from kanoi/master

USB locking
Con Kolivas 12 years ago
parent
commit
3f105a23c8
7 changed files with 237 additions and 153 deletions
  1. 24 20
      driver-avalon.c
  2. 15 30
      driver-bflsc.c
  3. 13 27
      driver-bitforce.c
  4. 6 20
      driver-icarus.c
  5. 25 42
      driver-modminer.c
  6. 137 14
      usbutils.c
  7. 17 0
      usbutils.h

+ 24 - 20
driver-avalon.c

@@ -574,39 +574,27 @@ static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found
 	int this_option_offset = ++option_offset;
 	struct avalon_info *info;
 	struct cgpu_info *avalon;
-	char devpath[20];
 	bool configured;
 	int ret;
 
-	avalon = calloc(1, sizeof(struct cgpu_info));
-	if (unlikely(!avalon))
-		quit(1, "Failed to calloc avalon in avalon_detect_one");;
-	avalon->drv = &avalon_drv;
-	avalon->threads = AVALON_MINER_THREADS;
+	avalon = usb_alloc_cgpu(&avalon_drv, AVALON_MINER_THREADS);
 
 	configured = get_options(this_option_offset, &baud, &miner_count,
 				 &asic_count, &timeout, &frequency);
 
 	if (!usb_init(avalon, dev, found))
-		return false;
+		goto shin;
 
 	/* Even though this is an FTDI type chip, we want to do the parsing
 	 * all ourselves so set it to std usb type */
 	avalon->usbdev->usb_type = USB_TYPE_STD;
 
 	/* We have a real Avalon! */
-	sprintf(devpath, "%d:%d",
-			(int)(avalon->usbinfo.bus_number),
-			(int)(avalon->usbinfo.device_address));
-
 	avalon_initialise(avalon);
 
-	avalon->device_path = strdup(devpath);
-	add_cgpu(avalon);
-
 	avalon->device_data = calloc(sizeof(struct avalon_info), 1);
 	if (unlikely(!(avalon->device_data)))
-		quit(1, "Failed to malloc avalon_info data");
+		quit(1, "Failed to calloc avalon_info data");
 	info = avalon->device_data;
 
 	if (configured) {
@@ -635,19 +623,35 @@ static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found
 	info->temp_old = 0;
 
 	ret = avalon_reset(avalon, true);
-	if (ret && !configured) {
-		usb_uninit(avalon);
-		return false;
-	}
+	if (ret && !configured)
+		goto unshin;
+
+	if (!add_cgpu(avalon))
+		goto unshin;
+
+	update_usb_stats(avalon);
 
 	avalon_idle(avalon, info);
 
 	applog(LOG_DEBUG, "Avalon Detected: %s "
 	       "(miner_count=%d asic_count=%d timeout=%d frequency=%d)",
-	       devpath, info->miner_count, info->asic_count, info->timeout,
+	       avalon->device_path, info->miner_count, info->asic_count, info->timeout,
 	       info->frequency);
 
 	return true;
+
+unshin:
+
+	usb_uninit(avalon);
+
+shin:
+
+	free(avalon->device_data);
+	avalon->device_data = NULL;
+
+	avalon = usb_free_cgpu(avalon);
+
+	return false;
 }
 
 static void avalon_detect(void)

+ 15 - 30
driver-bflsc.c

@@ -802,7 +802,6 @@ static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices
 {
 	struct bflsc_info *sc_info = NULL;
 	char buf[BFLSC_BUFSIZ+1];
-	char devpath[20];
 	int i, err, amount;
 	struct timeval init_start, init_now;
 	int init_sleep, init_count;
@@ -810,13 +809,7 @@ static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices
 	char *newname;
 	uint16_t latency;
 
-	struct cgpu_info *bflsc = calloc(1, sizeof(*bflsc));
-
-	if (unlikely(!bflsc))
-		quit(1, "Failed to calloc bflsc in bflsc_detect_one");
-	bflsc->drv = &bflsc_drv;
-	bflsc->deven = DEV_ENABLED;
-	bflsc->threads = 1;
+	struct cgpu_info *bflsc = usb_alloc_cgpu(&bflsc_drv, 1);
 
 	sc_info = calloc(1, sizeof(*sc_info));
 	if (unlikely(!sc_info))
@@ -827,11 +820,6 @@ static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices
 	if (!usb_init(bflsc, dev, found))
 		goto shin;
 
-	sprintf(devpath, "%d:%d",
-			(int)(bflsc->usbinfo.bus_number),
-			(int)(bflsc->usbinfo.device_address));
-
-
 	// Allow 2 complete attempts if the 1st time returns an unrecognised reply
 	ident_first = true;
 retry:
@@ -843,7 +831,7 @@ reinit:
 	err = write_to_dev(bflsc, 0, BFLSC_IDENTIFY, BFLSC_IDENTIFY_LEN, &amount, C_REQUESTIDENTIFY);
 	if (err < 0 || amount != BFLSC_IDENTIFY_LEN) {
 		applog(LOG_ERR, "%s detect (%s) send identify request failed (%d:%d)",
-			bflsc->drv->dname, devpath, amount, err);
+			bflsc->drv->dname, bflsc->device_path, amount, err);
 		goto unshin;
 	}
 
@@ -854,7 +842,7 @@ reinit:
 		if (us_tdiff(&init_now, &init_start) <= REINIT_TIME_MAX) {
 			if (init_count == 2) {
 				applog(LOG_WARNING, "%s detect (%s) 2nd init failed (%d:%d) - retrying",
-					bflsc->drv->dname, devpath, amount, err);
+					bflsc->drv->dname, bflsc->device_path, amount, err);
 			}
 			nmsleep(init_sleep);
 			if ((init_sleep * 2) <= REINIT_TIME_MAX_MS)
@@ -864,14 +852,14 @@ reinit:
 
 		if (init_count > 0)
 			applog(LOG_WARNING, "%s detect (%s) init failed %d times %.2fs",
-				bflsc->drv->dname, devpath, init_count, tdiff(&init_now, &init_start));
+				bflsc->drv->dname, bflsc->device_path, init_count, tdiff(&init_now, &init_start));
 
 		if (err < 0) {
 			applog(LOG_ERR, "%s detect (%s) error identify reply (%d:%d)",
-				bflsc->drv->dname, devpath, amount, err);
+				bflsc->drv->dname, bflsc->device_path, amount, err);
 		} else {
 			applog(LOG_ERR, "%s detect (%s) empty identify reply (%d)",
-				bflsc->drv->dname, devpath, amount);
+				bflsc->drv->dname, bflsc->device_path, amount);
 		}
 
 		goto unshin;
@@ -880,24 +868,22 @@ reinit:
 
 	if (unlikely(!strstr(buf, BFLSC_BFLSC))) {
 		applog(LOG_DEBUG, "%s detect (%s) found an FPGA '%s' ignoring",
-			bflsc->drv->dname, devpath, buf);
+			bflsc->drv->dname, bflsc->device_path, buf);
 		goto unshin;
 	}
 
 	if (unlikely(strstr(buf, BFLSC_IDENTITY))) {
 		if (ident_first) {
 			applog(LOG_DEBUG, "%s detect (%s) didn't recognise '%s' trying again ...",
-				bflsc->drv->dname, devpath, buf);
+				bflsc->drv->dname, bflsc->device_path, buf);
 			ident_first = false;
 			goto retry;
 		}
 		applog(LOG_DEBUG, "%s detect (%s) didn't recognise '%s' on 2nd attempt",
-			bflsc->drv->dname, devpath, buf);
+			bflsc->drv->dname, bflsc->device_path, buf);
 		goto unshin;
 	}
 
-	bflsc->device_path = strdup(devpath);
-
 	if (!getinfo(bflsc, 0))
 		goto unshin;
 
@@ -951,7 +937,7 @@ reinit:
 
 	// We have a real BFLSC!
 	applog(LOG_DEBUG, "%s (%s) identified as: '%s'",
-		bflsc->drv->dname, devpath, bflsc->drv->name);
+		bflsc->drv->dname, bflsc->device_path, bflsc->drv->name);
 
 	if (!add_cgpu(bflsc))
 		goto unshin;
@@ -971,16 +957,15 @@ unshin:
 
 shin:
 
-	free(bflsc->device_path);
 	free(bflsc->device_data);
+	bflsc->device_data = NULL;
 
-	if (bflsc->name != blank)
+	if (bflsc->name != blank) {
 		free(bflsc->name);
+		bflsc->name = NULL;
+	}
 
-	if (bflsc->drv->copy)
-		free(bflsc->drv);
-
-	free(bflsc);
+	bflsc = usb_free_cgpu(bflsc);
 
 	return false;
 }

+ 13 - 27
driver-bitforce.c

@@ -166,26 +166,17 @@ failed:
 static bool bitforce_detect_one(struct libusb_device *dev, struct usb_find_devices *found)
 {
 	char buf[BITFORCE_BUFSIZ+1];
-	char devpath[20];
 	int err, amount;
 	char *s;
 	struct timeval init_start, init_now;
 	int init_sleep, init_count;
 	bool ident_first;
 
-	struct cgpu_info *bitforce = NULL;
-	bitforce = calloc(1, sizeof(*bitforce));
-	bitforce->drv = &bitforce_drv;
-	bitforce->deven = DEV_ENABLED;
-	bitforce->threads = 1;
+	struct cgpu_info *bitforce = usb_alloc_cgpu(&bitforce_drv, 1);
 
 	if (!usb_init(bitforce, dev, found))
 		goto shin;
 
-	sprintf(devpath, "%d:%d",
-			(int)(bitforce->usbinfo.bus_number),
-			(int)(bitforce->usbinfo.device_address));
-
 	// Allow 2 complete attempts if the 1st time returns an unrecognised reply
 	ident_first = true;
 retry:
@@ -196,7 +187,7 @@ reinit:
 	bitforce_initialise(bitforce, false);
 	if ((err = usb_write(bitforce, BITFORCE_IDENTIFY, BITFORCE_IDENTIFY_LEN, &amount, C_REQUESTIDENTIFY)) < 0 || amount != BITFORCE_IDENTIFY_LEN) {
 		applog(LOG_ERR, "%s detect (%s) send identify request failed (%d:%d)",
-			bitforce->drv->dname, devpath, amount, err);
+			bitforce->drv->dname, bitforce->device_path, amount, err);
 		goto unshin;
 	}
 
@@ -206,7 +197,7 @@ reinit:
 		if (us_tdiff(&init_now, &init_start) <= REINIT_TIME_MAX) {
 			if (init_count == 2) {
 				applog(LOG_WARNING, "%s detect (%s) 2nd init failed (%d:%d) - retrying",
-					bitforce->drv->dname, devpath, amount, err);
+					bitforce->drv->dname, bitforce->device_path, amount, err);
 			}
 			nmsleep(init_sleep);
 			if ((init_sleep * 2) <= REINIT_TIME_MAX_MS)
@@ -216,14 +207,14 @@ reinit:
 
 		if (init_count > 0)
 			applog(LOG_WARNING, "%s detect (%s) init failed %d times %.2fs",
-				bitforce->drv->dname, devpath, init_count, tdiff(&init_now, &init_start));
+				bitforce->drv->dname, bitforce->device_path, init_count, tdiff(&init_now, &init_start));
 
 		if (err < 0) {
 			applog(LOG_ERR, "%s detect (%s) error identify reply (%d:%d)",
-				bitforce->drv->dname, devpath, amount, err);
+				bitforce->drv->dname, bitforce->device_path, amount, err);
 		} else {
 			applog(LOG_ERR, "%s detect (%s) empty identify reply (%d)",
-				bitforce->drv->dname, devpath, amount);
+				bitforce->drv->dname, bitforce->device_path, amount);
 		}
 
 		goto unshin;
@@ -233,12 +224,12 @@ reinit:
 	if (unlikely(!strstr(buf, "SHA256"))) {
 		if (ident_first) {
 			applog(LOG_WARNING, "%s detect (%s) didn't recognise '%s' trying again ...",
-				bitforce->drv->dname, devpath, buf);
+				bitforce->drv->dname, bitforce->device_path, buf);
 			ident_first = false;
 			goto retry;
 		}
 		applog(LOG_ERR, "%s detect (%s) didn't recognise '%s' on 2nd attempt",
-			bitforce->drv->dname, devpath, buf);
+			bitforce->drv->dname, bitforce->device_path, buf);
 		goto unshin;
 	}
 
@@ -260,7 +251,7 @@ reinit:
 
 	// We have a real BitForce!
 	applog(LOG_DEBUG, "%s (%s) identified as: '%s'",
-		bitforce->drv->dname, devpath, bitforce->name);
+		bitforce->drv->dname, bitforce->device_path, bitforce->name);
 
 	/* Initially enable support for nonce range and disable it later if it
 	 * fails */
@@ -273,8 +264,6 @@ reinit:
 		bitforce->kname = KNAME_WORK;
 	}
 
-	bitforce->device_path = strdup(devpath);
-
 	if (!add_cgpu(bitforce))
 		goto unshin;
 
@@ -290,15 +279,12 @@ unshin:
 
 shin:
 
-	free(bitforce->device_path);
-
-	if (bitforce->name != blank)
+	if (bitforce->name != blank) {
 		free(bitforce->name);
+		bitforce->name = NULL;
+	}
 
-	if (bitforce->drv->copy)
-		free(bitforce->drv);
-
-	free(bitforce);
+	bitforce = usb_free_cgpu(bitforce);
 
 	return false;
 }

+ 6 - 20
driver-icarus.c

@@ -445,7 +445,7 @@ static int icarus_get_nonce(struct cgpu_info *icarus, unsigned char *buf, struct
 		if (amt >= read_amount)
 			return ICA_NONCE_OK;
 
-		rc += SECTOMS(tdiff(&read_finish, &read_start));
+		rc = SECTOMS(tdiff(&read_finish, tv_start));
 		if (rc >= read_time) {
 			if (amt > 0)
 				applog(LOG_DEBUG, "Icarus Read: Timeout reading for %d ms", rc);
@@ -728,7 +728,6 @@ static void get_options(int this_option_offset, struct cgpu_info *icarus, int *b
 static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices *found)
 {
 	int this_option_offset = ++option_offset;
-	char devpath[20];
 	struct ICARUS_INFO *info;
 	struct timeval tv_start, tv_finish;
 
@@ -751,24 +750,13 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 	int ret, err, amount, tries;
 	bool ok;
 
-	icarus = calloc(1, sizeof(struct cgpu_info));
-	if (unlikely(!icarus))
-		quit(1, "Failed to calloc icarus in icarus_detect_one");
-	icarus->drv = &icarus_drv;
-	icarus->deven = DEV_ENABLED;
-	icarus->threads = 1;
+	icarus = usb_alloc_cgpu(&icarus_drv, 1);
 
 	if (!usb_init(icarus, dev, found))
 		goto shin;
 
 	get_options(this_option_offset, icarus, &baud, &work_division, &fpga_count);
 
-	sprintf(devpath, "%d:%d",
-			(int)(icarus->usbinfo.bus_number),
-			(int)(icarus->usbinfo.device_address));
-
-	icarus->device_path = strdup(devpath);
-
 	hex2bin(ob_bin, golden_ob, sizeof(ob_bin));
 
 	tries = 2;
@@ -794,7 +782,7 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 				applog(LOG_ERR,
 					"Icarus Detect: "
 					"Test failed at %s: get %s, should: %s",
-					devpath, nonce_hex, golden_nonce);
+					icarus->device_path, nonce_hex, golden_nonce);
 			}
 		}
 		free(nonce_hex);
@@ -806,7 +794,7 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 	applog(LOG_DEBUG,
 		"Icarus Detect: "
 		"Test succeeded at %s: got %s",
-			devpath, golden_nonce);
+			icarus->device_path, golden_nonce);
 
 	/* We have a real Icarus! */
 	if (!add_cgpu(icarus))
@@ -815,7 +803,7 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 	update_usb_stats(icarus);
 
 	applog(LOG_INFO, "%s%d: Found at %s",
-		icarus->drv->name, icarus->device_id, devpath);
+		icarus->drv->name, icarus->device_id, icarus->device_path);
 
 	applog(LOG_DEBUG, "%s%d: Init baud=%d work_division=%d fpga_count=%d",
 		icarus->drv->name, icarus->device_id, baud, work_division, fpga_count);
@@ -847,11 +835,9 @@ unshin:
 
 	usb_uninit(icarus);
 
-	free(icarus->device_path);
-
 shin:
 
-	free(icarus);
+	icarus = usb_free_cgpu(icarus);
 
 	return false;
 }

+ 25 - 42
driver-modminer.c

@@ -115,13 +115,12 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 {
 	char buf[0x100+1];
 	char *devname = NULL;
-	char devpath[20];
+	char devpath[32];
 	int err, i, amount;
 	bool added = false;
 
-	struct cgpu_info *modminer = NULL;
-	modminer = calloc(1, sizeof(*modminer));
-	modminer->drv = &modminer_drv;
+	struct cgpu_info *modminer = usb_alloc_cgpu(&modminer_drv, 1);
+
 	modminer->modminer_mutex = calloc(1, sizeof(*(modminer->modminer_mutex)));
 	mutex_init(modminer->modminer_mutex);
 	modminer->fpgaid = (char)0;
@@ -129,44 +128,40 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 	if (!usb_init(modminer, dev, found))
 		goto shin;
 
-	sprintf(devpath, "%d:%d",
-			(int)(modminer->usbinfo.bus_number),
-			(int)(modminer->usbinfo.device_address));
-
 	do_ping(modminer);
 
 	if ((err = usb_write(modminer, MODMINER_GET_VERSION, 1, &amount, C_REQUESTVERSION)) < 0 || amount != 1) {
 		applog(LOG_ERR, "%s detect (%s) send version request failed (%d:%d)",
-			modminer->drv->dname, devpath, amount, err);
+			modminer->drv->dname, modminer->device_path, amount, err);
 		goto unshin;
 	}
 
 	if ((err = usb_read_once(modminer, buf, sizeof(buf)-1, &amount, C_GETVERSION)) < 0 || amount < 1) {
 		if (err < 0)
 			applog(LOG_ERR, "%s detect (%s) no version reply (%d)",
-				modminer->drv->dname, devpath, err);
+				modminer->drv->dname, modminer->device_path, err);
 		else
 			applog(LOG_ERR, "%s detect (%s) empty version reply (%d)",
-				modminer->drv->dname, devpath, amount);
+				modminer->drv->dname, modminer->device_path, amount);
 
 		applog(LOG_DEBUG, "%s detect (%s) check the firmware",
-				modminer->drv->dname, devpath);
+				modminer->drv->dname, modminer->device_path);
 
 		goto unshin;
 	}
 	buf[amount] = '\0';
 	devname = strdup(buf);
-	applog(LOG_DEBUG, "%s (%s) identified as: %s", modminer->drv->dname, devpath, devname);
+	applog(LOG_DEBUG, "%s (%s) identified as: %s", modminer->drv->dname, modminer->device_path, devname);
 
 	if ((err = usb_write(modminer, MODMINER_FPGA_COUNT, 1, &amount, C_REQUESTFPGACOUNT) < 0 || amount != 1)) {
 		applog(LOG_ERR, "%s detect (%s) FPGA count request failed (%d:%d)",
-			modminer->drv->dname, devpath, amount, err);
+			modminer->drv->dname, modminer->device_path, amount, err);
 		goto unshin;
 	}
 
 	if ((err = usb_read(modminer, buf, 1, &amount, C_GETFPGACOUNT)) < 0 || amount != 1) {
 		applog(LOG_ERR, "%s detect (%s) no FPGA count reply (%d:%d)",
-			modminer->drv->dname, devpath, amount, err);
+			modminer->drv->dname, modminer->device_path, amount, err);
 		goto unshin;
 	}
 
@@ -175,28 +170,25 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 
 	if (buf[0] == 0) {
 		applog(LOG_ERR, "%s detect (%s) zero FPGA count from %s",
-			modminer->drv->dname, devpath, devname);
+			modminer->drv->dname, modminer->device_path, devname);
 		goto unshin;
 	}
 
 	if (buf[0] < 1 || buf[0] > 4) {
 		applog(LOG_ERR, "%s detect (%s) invalid FPGA count (%u) from %s",
-			modminer->drv->dname, devpath, buf[0], devname);
+			modminer->drv->dname, modminer->device_path, buf[0], devname);
 		goto unshin;
 	}
 
 	applog(LOG_DEBUG, "%s (%s) %s has %u FPGAs",
-		modminer->drv->dname, devpath, devname, buf[0]);
+		modminer->drv->dname, modminer->device_path, devname, buf[0]);
 
 	modminer->name = devname;
 
 	// TODO: test with 1 board missing in the middle and each end
 	// to see how that affects the sequence numbers
 	for (i = 0; i < buf[0]; i++) {
-		struct cgpu_info *tmp = calloc(1, sizeof(*tmp));
-
-		tmp->drv = copy_drv(modminer->drv);
-		tmp->name = devname;
+		struct cgpu_info *tmp = usb_copy_cgpu(modminer);
 
 		sprintf(devpath, "%d:%d:%d",
 			(int)(modminer->usbinfo.bus_number),
@@ -204,22 +196,16 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 			i);
 
 		tmp->device_path = strdup(devpath);
-		tmp->usbdev = modminer->usbdev;
-		tmp->usbinfo.bus_number = modminer->usbinfo.bus_number;
-		tmp->usbinfo.device_address = modminer->usbinfo.device_address;
+
 		// Only the first copy gets the already used stats
-		if (!added)
-			tmp->usbinfo.usbstat = modminer->usbinfo.usbstat;
+		if (added)
+			tmp->usbinfo.usbstat = USB_NOSTAT;
+
 		tmp->fpgaid = (char)i;
 		tmp->modminer_mutex = modminer->modminer_mutex;
-		tmp->deven = DEV_ENABLED;
-		tmp->threads = 1;
 
 		if (!add_cgpu(tmp)) {
-			free(tmp->device_path);
-			if (tmp->drv->copy)
-				free(tmp->drv);
-			free(tmp);
+			tmp = usb_free_cgpu_devlock(tmp, !added);
 			goto unshin;
 		}
 
@@ -228,10 +214,7 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 		added = true;
 	}
 
-	if (modminer->drv->copy)
-		free(modminer->drv);
-
-	free(modminer);
+	modminer = usb_free_cgpu_devlock(modminer, !added);
 
 	return true;
 
@@ -240,13 +223,12 @@ unshin:
 		usb_uninit(modminer);
 
 shin:
-	if (!added)
+	if (!added) {
 		free(modminer->modminer_mutex);
+		modminer->modminer_mutex = NULL;
+	}
 
-	if (modminer->drv->copy)
-		free(modminer->drv);
-
-	free(modminer);
+	modminer = usb_free_cgpu_devlock(modminer, !added);
 
 	if (added)
 		return true;
@@ -1111,6 +1093,7 @@ static void modminer_hw_error(struct thr_info *thr)
 static void modminer_fpga_shutdown(struct thr_info *thr)
 {
 	free(thr->cgpu_data);
+	thr->cgpu_data = NULL;
 }
 
 static char *modminer_set_device(struct cgpu_info *modminer, char *option, char *setting, char *replybuf)

+ 137 - 14
usbutils.c

@@ -451,7 +451,7 @@ struct cg_usb_stats {
 #define SEQ1 1
 
 static struct cg_usb_stats *usb_stats = NULL;
-static int next_stat = 0;
+static int next_stat = USB_NOSTAT;
 
 #define USB_STATS(sgpu, sta, fin, err, mode, cmd, seq) \
 		stats(cgpu, sta, fin, err, mode, cmd, seq)
@@ -1339,6 +1339,10 @@ void usb_uninit(struct cgpu_info *cgpu)
 	cgpu->usbdev = free_cgusb(cgpu->usbdev);
 }
 
+/*
+ * N.B. this is always called inside
+ *	DEVLOCK(cgpu);
+ */
 static void release_cgpu(struct cgpu_info *cgpu)
 {
 	struct cg_usb_device *cgusb = cgpu->usbdev;
@@ -1380,10 +1384,84 @@ 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
+struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig)
+{
+	struct cgpu_info *copy = calloc(1, sizeof(*copy));
+
+	if (unlikely(!copy))
+		quit(1, "Failed to calloc cgpu for %s in usb_copy_cgpu", orig->drv->dname);
+
+	copy->name = orig->name;
+	copy->drv = copy_drv(orig->drv);
+	copy->deven = orig->deven;
+	copy->threads = orig->threads;
+
+	copy->usbdev = orig->usbdev;
+
+	memcpy(&(copy->usbinfo), &(orig->usbinfo), sizeof(copy->usbinfo));
+
+	copy->usbinfo.nodev = (copy->usbdev == NULL);
+
+	copy->usbinfo.devlock = orig->usbinfo.devlock;
+
+	return copy;
+}
+
+struct cgpu_info *usb_alloc_cgpu(struct device_drv *drv, int threads)
+{
+	struct cgpu_info *cgpu = calloc(1, sizeof(*cgpu));
+
+	if (unlikely(!cgpu))
+		quit(1, "Failed to calloc cgpu for %s in usb_alloc_cgpu", drv->dname);
+
+	cgpu->drv = drv;
+	cgpu->deven = DEV_ENABLED;
+	cgpu->threads = threads;
+
+	cgpu->usbinfo.nodev = true;
+
+	cgpu->usbinfo.devlock = calloc(1, sizeof(*(cgpu->usbinfo.devlock)));
+	if (unlikely(!cgpu->usbinfo.devlock))
+		quit(1, "Failed to calloc devlock for %s in usb_alloc_cgpu", drv->dname);
+
+	rwlock_init(cgpu->usbinfo.devlock);
+
+	return cgpu;
+}
+
+struct cgpu_info *usb_free_cgpu_devlock(struct cgpu_info *cgpu, bool free_devlock)
+{
+	if (cgpu->drv->copy)
+		free(cgpu->drv);
+
+	free(cgpu->device_path);
+
+	if (free_devlock)
+		free(cgpu->usbinfo.devlock);
+
+	free(cgpu);
+
+	return NULL;
+}
+
 #define USB_INIT_FAIL 0
 #define USB_INIT_OK 1
 #define USB_INIT_IGNORE 2
 
+/*
+ * WARNING - these assume DEVLOCK(cgpu) is called first and
+ *  DEVUNLOCK(cgpu) in called in the same function inside the same brace level
+ *  You must call DEVUNLOCK(cgpu) before exiting the function or it will leave
+ *  the thread Cancelability unrestored
+ */
+#define DEVLOCK(cgpu) int _pth_state; \
+			pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &_pth_state); \
+			wr_lock(cgpu->usbinfo.devlock);
+
+#define DEVUNLOCK(cgpu) wr_unlock(cgpu->usbinfo.devlock); \
+			pthread_setcanceltype(_pth_state, NULL);
+
 static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found)
 {
 	struct cg_usb_device *cgusb = NULL;
@@ -1391,15 +1469,23 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u
 	const struct libusb_interface_descriptor *idesc;
 	const struct libusb_endpoint_descriptor *epdesc;
 	unsigned char strbuf[STRBUFLEN+1];
+	char devpath[32];
 	char devstr[STRBUFLEN+1];
 	int err, i, j, k;
 	int bad = USB_INIT_FAIL;
 
+	DEVLOCK(cgpu);
+
 	cgpu->usbinfo.bus_number = libusb_get_bus_number(dev);
 	cgpu->usbinfo.device_address = libusb_get_device_address(dev);
 
-	sprintf(devstr, "- %s device %d:%d", found->name,
-		cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address);
+	sprintf(devpath, "%d:%d",
+		(int)(cgpu->usbinfo.bus_number),
+		(int)(cgpu->usbinfo.device_address));
+
+	cgpu->device_path = strdup(devpath);
+
+	sprintf(devstr, "- %s device %s", found->name, devpath);
 
 	cgusb = calloc(1, sizeof(*cgusb));
 	if (unlikely(!cgusb))
@@ -1616,6 +1702,7 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u
 		cgusb->manuf_string, cgusb->serial_string);
 
 	cgpu->usbdev = cgusb;
+	cgpu->usbinfo.nodev = false;
 
 	libusb_free_config_descriptor(config);
 
@@ -1627,6 +1714,8 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u
 		cgpu->drv->name = (char *)(found->name);
 	}
 
+	DEVUNLOCK(cgpu);
+
 	return USB_INIT_OK;
 
 cldame:
@@ -1640,6 +1729,8 @@ dame:
 
 	cgusb = free_cgusb(cgusb);
 
+	DEVUNLOCK(cgpu);
+
 	return bad;
 }
 
@@ -1876,7 +1967,7 @@ struct api_data *api_usb_stats(__maybe_unused int *count)
 	int cmdseq;
 	char modes_s[32];
 
-	if (next_stat == 0)
+	if (next_stat == USB_NOSTAT)
 		return NULL;
 
 	while (*count < next_stat * C_MAX * 2) {
@@ -1949,21 +2040,25 @@ static void newstats(struct cgpu_info *cgpu)
 	int i;
 
 	mutex_lock(&cgusb_lock);
-	cgpu->usbinfo.usbstat = ++next_stat;
-	mutex_unlock(&cgusb_lock);
 
-	usb_stats = realloc(usb_stats, sizeof(*usb_stats) * next_stat);
+	cgpu->usbinfo.usbstat = next_stat + 1;
+
+	usb_stats = realloc(usb_stats, sizeof(*usb_stats) * (next_stat+1));
 	if (unlikely(!usb_stats))
-		quit(1, "USB failed to realloc usb_stats %d", next_stat);
+		quit(1, "USB failed to realloc usb_stats %d", next_stat+1);
 
-	usb_stats[next_stat-1].name = cgpu->drv->name;
-	usb_stats[next_stat-1].device_id = -1;
-	usb_stats[next_stat-1].details = calloc(1, sizeof(struct cg_usb_stats_details) * C_MAX * 2);
-	if (unlikely(!usb_stats[next_stat-1].details))
-		quit(1, "USB failed to calloc details for %d", next_stat);
+	usb_stats[next_stat].name = cgpu->drv->name;
+	usb_stats[next_stat].device_id = -1;
+	usb_stats[next_stat].details = calloc(1, sizeof(struct cg_usb_stats_details) * C_MAX * 2);
+	if (unlikely(!usb_stats[next_stat].details))
+		quit(1, "USB failed to calloc details for %d", next_stat+1);
 
 	for (i = 1; i < C_MAX * 2; i += 2)
-		usb_stats[next_stat-1].details[i].seq = 1;
+		usb_stats[next_stat].details[i].seq = 1;
+
+	next_stat++;
+
+	mutex_unlock(&cgusb_lock);
 }
 #endif
 
@@ -2084,11 +2179,15 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 	unsigned char usbbuf[USB_MAX_READ+4], *ptr;
 	size_t usbbufread;
 
+	DEVLOCK(cgpu);
+
 	if (cgpu->usbinfo.nodev) {
 		*buf = '\0';
 		*processed = 0;
 		USB_REJECT(cgpu, MODE_BULK_READ);
 
+		DEVUNLOCK(cgpu);
+
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
 
@@ -2191,6 +2290,8 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 		if (NODEV(err))
 			release_cgpu(cgpu);
 
+		DEVUNLOCK(cgpu);
+
 		return err;
 	}
 
@@ -2306,6 +2407,8 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 	if (NODEV(err))
 		release_cgpu(cgpu);
 
+	DEVUNLOCK(cgpu);
+
 	return err;
 }
 
@@ -2321,6 +2424,8 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr
 	__maybe_unused bool first = true;
 	int err, sent, tot;
 
+	DEVLOCK(cgpu);
+
 	USBDEBUG("USB debug: _usb_write(%s (nodev=%s),ep=%d,buf='%s',bufsiz=%zu,proc=%p,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), ep, (char *)str_text(buf), bufsiz, processed, timeout, usb_cmdname(cmd));
 
 	*processed = 0;
@@ -2328,6 +2433,8 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr
 	if (cgpu->usbinfo.nodev) {
 		USB_REJECT(cgpu, MODE_BULK_WRITE);
 
+		DEVUNLOCK(cgpu);
+
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
 
@@ -2380,6 +2487,8 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr
 	if (NODEV(err))
 		release_cgpu(cgpu);
 
+	DEVUNLOCK(cgpu);
+
 	return err;
 }
 
@@ -2392,11 +2501,15 @@ int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest
 	uint32_t *buf = NULL;
 	int err, i, bufsiz;
 
+	DEVLOCK(cgpu);
+
 	USBDEBUG("USB debug: _usb_transfer(%s (nodev=%s),type=%"PRIu8",req=%"PRIu8",value=%"PRIu16",index=%"PRIu16",siz=%d,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), request_type, bRequest, wValue, wIndex, siz, timeout, usb_cmdname(cmd));
 
 	if (cgpu->usbinfo.nodev) {
 		USB_REJECT(cgpu, MODE_CTRL_WRITE);
 
+		DEVUNLOCK(cgpu);
+
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
 	usbdev = cgpu->usbdev;
@@ -2433,6 +2546,8 @@ int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest
 	if (NODEV(err))
 		release_cgpu(cgpu);
 
+	DEVUNLOCK(cgpu);
+
 	return err;
 }
 
@@ -2444,11 +2559,15 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe
 #endif
 	int err;
 
+	DEVLOCK(cgpu);
+
 	USBDEBUG("USB debug: _usb_transfer_read(%s (nodev=%s),type=%"PRIu8",req=%"PRIu8",value=%"PRIu16",index=%"PRIu16",bufsiz=%d,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), request_type, bRequest, wValue, wIndex, bufsiz, timeout, usb_cmdname(cmd));
 
 	if (cgpu->usbinfo.nodev) {
 		USB_REJECT(cgpu, MODE_CTRL_READ);
 
+		DEVUNLOCK(cgpu);
+
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
 	usbdev = cgpu->usbdev;
@@ -2473,6 +2592,8 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe
 	} else if (NODEV(err))
 		release_cgpu(cgpu);
 
+	DEVUNLOCK(cgpu);
+
 	return err;
 }
 
@@ -2586,7 +2707,9 @@ void usb_cleanup()
 			case DRIVER_MODMINER:
 			case DRIVER_ICARUS:
 			case DRIVER_AVALON:
+				wr_lock(cgpu->usbinfo.devlock);
 				release_cgpu(cgpu);
+				wr_unlock(cgpu->usbinfo.devlock);
 				count++;
 				break;
 			default:

+ 17 - 0
usbutils.h

@@ -177,6 +177,8 @@ struct cg_usb_device {
 	uint32_t bufamt;
 };
 
+#define USB_NOSTAT 0
+
 struct cg_usb_info {
 	uint8_t bus_number;
 	uint8_t device_address;
@@ -186,6 +188,17 @@ struct cg_usb_info {
 	struct timeval last_nodev;
 	uint32_t ioerr_count;
 	uint32_t continuous_ioerr_count;
+
+	/*
+	 * for nodev and cgusb access (read and write)
+	 * it's a pointer so MMQ can have it in multiple devices
+	 *
+	 * N.B. general mining code doesn't need to use the read
+	 * lock for 'nodev' if it calls a usb_read/write/etc function
+	 * that uses the lock - however, all usbutils code MUST use it
+	 * to avoid devices disappearing while in use by multiple threads
+	 */
+	pthread_rwlock_t *devlock;
 };
 
 enum usb_cmds {
@@ -258,6 +271,10 @@ struct cgpu_info;
 void usb_all(int level);
 const char *usb_cmdname(enum usb_cmds cmd);
 void usb_applog(struct cgpu_info *bflsc, enum usb_cmds cmd, char *msg, int amount, int err);
+struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig);
+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);
 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 *));