Browse Source

Merge branch 'master' into libusbx

Conflicts:
	api.c
	cgminer.c
	miner.h
	usbutils.c
Con Kolivas 12 years ago
parent
commit
64f5cac62c
11 changed files with 467 additions and 113 deletions
  1. 7 6
      API-README
  2. 93 0
      NEWS
  3. 0 1
      api.c
  4. 31 11
      cgminer.c
  5. 1 0
      driver-avalon.c
  6. 52 22
      driver-bitfury.c
  7. 3 0
      driver-bitfury.h
  8. 217 22
      driver-icarus.c
  9. 8 1
      miner.h
  10. 54 49
      usbutils.c
  11. 1 1
      util.c

+ 7 - 6
API-README

@@ -277,13 +277,13 @@ The list of requests - a (*) means it requires privileged access - and replies:
                none           There is no reply section just the STATUS section
                none           There is no reply section just the STATUS section
                               stating the results of the identify request
                               stating the results of the identify request
                               This is only available if PGA mining is enabled
                               This is only available if PGA mining is enabled
-                              and currently only BFL singles support this
-                              command
+                              and currently only BFL singles and Cairnsmore1's
+                              with the appropriate firmware support this command
                               On a BFL single it will flash the led on the front
                               On a BFL single it will flash the led on the front
                               of the device for appoximately 4s
                               of the device for appoximately 4s
-                              All other non BFL PGA devices will return a
+                              All other non BFL,ICA PGA devices will return a
                               warning status message stating that they dont
                               warning status message stating that they dont
-                              support it
+                              support it. Non-CMR ICAs will ignore the command.
                               This adds a 4s delay to the BFL share being
                               This adds a 4s delay to the BFL share being
                               processed so you may get a message stating that
                               processed so you may get a message stating that
                               procssing took longer than 7000ms if the request
                               procssing took longer than 7000ms if the request
@@ -364,6 +364,7 @@ The list of requests - a (*) means it requires privileged access - and replies:
 
 
                               The current options are:
                               The current options are:
                                MMQ opt=clock val=160 to 230 (a multiple of 2)
                                MMQ opt=clock val=160 to 230 (a multiple of 2)
+                               CMR opt=clock val=100 to 220
 
 
  zero|Which,true/false (*)
  zero|Which,true/false (*)
                none           There is no reply section just the STATUS section
                none           There is no reply section just the STATUS section
@@ -433,8 +434,8 @@ The list of requests - a (*) means it requires privileged access - and replies:
                               help message about the options available
                               help message about the options available
 
 
                               The current options are:
                               The current options are:
-                               AVA+BTB opt=freq val=256 to 450 - chip frequency
-                               BTB opt=millivolts val=1000 to 1310 - corevoltage
+                               AVA+BTB opt=freq val=256 to 1024 - chip frequency
+                               BTB opt=millivolts val=1000 to 1400 - corevoltage
 
 
 When you enable, disable or restart a GPU, PGA or ASC, you will also get
 When you enable, disable or restart a GPU, PGA or ASC, you will also get
 Thread messages in the cgminer status window
 Thread messages in the cgminer status window

+ 93 - 0
NEWS

@@ -1,3 +1,96 @@
+
+- We are always dependent on libusb handling events so use the blocking
+libusb_handle_events in the polling thread and use a bool to know if we should
+continue polling.
+- Use fractional hashrate return values in bitfury_scanhash to minimise the
+number of times we return 0 based on hashrate so far to further damp out
+displayed hashrate.
+- Check for presence of driver name in DRIVER_COUNT_FOUND to prevent strcmp on a
+null pointer when a driver is not built in.
+- CMR allow sending flash and clock commands
+- Kill off threads that have failed using hash_sole_work instead of just
+disabling them.
+- Make the bf1 getinfo size a macro
+- Failing to add_cgpu in bitfury should be a terminal failure.
+- Check return values when attempting to open a BF1 device and set the msg size
+as a macro.
+- Display errors on failed usb read and write and consider sequential IO errors
+a permanent failure.
+- Use libusb's own error name function instead of hand coding the error names.
+- Limit ms_tdiff to 1 hour as a sanity check.
+- Enable the usb buffer in avalon driver.
+- Check for async transfer variants of error messages.
+- Remove unused variables.
+- Try switching pools if for some reason we end up with only idle pools and have
+ended up current_pool set to an idle one.
+- Check a pool is stable for >5 mins before switching back to it.
+- Minimise the time between dropping the read devlock and grabbing the write
+devlock to avoid tons of logging spam in the interim.
+- Check for libusb transfer stall error to be consistent with async IO errors
+returned for a halt condition.
+- Check for continuous IO errors on USB and consider the device inactive if more
+than retry max.
+- Make the devlock a cglock in usbutils and only grab the write lock for
+fundamental changes allowing us to send and receive transfers concurrently
+without lock contention.
+- Prevent overflows in us_tdiff and ms_tdiff.
+- Change second initialise message on bitfury verbose mode.
+- Submitting an ntime offset nonce needs to be done on a copy of the work
+instead of the original so abstract out shared components as much as possible,
+minimising strdups in copy_work and make submit_work_async work take copied
+work, cleaning up code in the process.
+- Provide a way for drivers to submit work that it has internally rolled the
+ntime value by returning the amount it has ntime rolled to be added.
+- Typo in configure.ac
+- Remove unmaintained broken ztex driver.
+- Icarus - use a data structure for I/O rather than magic numbers
+- delete old tracked ccan/opt/*.o files
+- klondike correct cvtKlnToC() temperature calculation
+- klondike - correct 1st reply debug based on define
+- klondike - debug dump structured replies
+- klondike - avoid division by zero if maxcount is unexpectedly zero
+- klondike store and report errorcount and noise
+- klondike - fix chipstats api stats buffer overrun with 16 chips
+- klondike add new nonecount only once
+- klondike - report mh/s based on nonces found + put old estimate into API stats
+- klondike use a memcpy
+- klondike fix bracket tabs indenting
+- api.c missing Klondike from ASIC list
+- Klondike update code to current git
+- Add 2nd CMR to 01-cgminer.rules
+- Add Klondike to 01-cgminer.rules
+- Klondike to main directory
+- Klondike consistent code spacing
+- Klondike update driver code to current git
+- update firmware for 16 chips, add dist files
+- beta final 0.3.0 release
+- updated firmware, IOC method
+- prevent nonces when not state W
+- added driver config option support
+- fixes for 300 MHz, fix K1 parts list
+- update driver, docs
+- update firmware & utils
+- updated cgminer driver for 3.3.1
+- update firmware and driver, create new cgminer fork
+- update klondike driver
+- add cgminer driver file as-is
+- Add API output displaying USB cancellations.
+- Store statistics on how often we have to cancel async bulk transfers and add a
+debug message whenever we do.
+- Treat any unexpected timeouts waiting for async transfers as though there may
+be a usb halt condition and attempt to clear the halt before cancelling the
+tranfer.
+- Remove zero packet flag on usb as it's unsupported outside linux and
+unnecessary.
+- Fake the libusb transfer timed out message if we force cancel it with our own
+async functions.
+- Use asynchronous transfers for all bulk transfers, allowing us to use our own
+timers and cancelling transfers that take too long.
+- Add libusb error warning message when significant error occurs.
+- Icarus CMR2 detect FPGA setup
+- Disable bitfury device thread on it disappearing.
+
+
 Version 3.5.0 - 29th September 2013
 Version 3.5.0 - 29th September 2013
 
 
 - Add magic init sequence required on BF1 devices to get them mining on windows.
 - Add magic init sequence required on BF1 devices to get them mining on windows.

+ 0 - 1
api.c

@@ -3141,7 +3141,6 @@ static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_st
 		}
 		}
 
 
 		root = api_add_string(root, "USB tmo", details, true);
 		root = api_add_string(root, "USB tmo", details, true);
-		root = api_add_int(root, "USB cancellations", &cgpu->usb_cancels, false);
 #endif
 #endif
 	}
 	}
 
 

+ 31 - 11
cgminer.c

@@ -175,6 +175,8 @@ char *opt_usb_select = NULL;
 int opt_usbdump = -1;
 int opt_usbdump = -1;
 bool opt_usb_list_all;
 bool opt_usb_list_all;
 cgsem_t usb_resource_sem;
 cgsem_t usb_resource_sem;
+static pthread_t usb_poll_thread;
+static bool usb_polling;
 #endif
 #endif
 
 
 char *opt_kernel_path;
 char *opt_kernel_path;
@@ -3216,6 +3218,8 @@ static void __kill_work(void)
 	/* Release USB resources in case it's a restart
 	/* Release USB resources in case it's a restart
 	 * and not a QUIT */
 	 * and not a QUIT */
 	if (!opt_scrypt) {
 	if (!opt_scrypt) {
+		usb_polling = false;
+
 		applog(LOG_DEBUG, "Releasing all USB devices");
 		applog(LOG_DEBUG, "Releasing all USB devices");
 		usb_cleanup();
 		usb_cleanup();
 
 
@@ -5786,10 +5790,9 @@ out:
 
 
 static void pool_resus(struct pool *pool)
 static void pool_resus(struct pool *pool)
 {
 {
-	if (pool_strategy == POOL_FAILOVER && pool->prio < cp_prio()) {
-		applog(LOG_WARNING, "Pool %d %s alive", pool->pool_no, pool->rpc_url);
-		switch_pools(NULL);
-	} else
+	if (pool_strategy == POOL_FAILOVER && pool->prio < cp_prio())
+		applog(LOG_WARNING, "Pool %d %s alive, testing stability", pool->pool_no, pool->rpc_url);
+	else
 		applog(LOG_INFO, "Pool %d %s alive", pool->pool_no, pool->rpc_url);
 		applog(LOG_INFO, "Pool %d %s alive", pool->pool_no, pool->rpc_url);
 }
 }
 
 
@@ -6255,7 +6258,8 @@ static void hash_sole_work(struct thr_info *mythr)
 				applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id);
 				applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id);
 				cgpu->deven = DEV_DISABLED;
 				cgpu->deven = DEV_DISABLED;
 				dev_error(cgpu, REASON_THREAD_ZERO_HASH);
 				dev_error(cgpu, REASON_THREAD_ZERO_HASH);
-				mt_disable(mythr, thr_id, drv);
+				cgpu->shutdown = true;
+				break;
 			}
 			}
 
 
 			hashes_done += hashes;
 			hashes_done += hashes;
@@ -6867,8 +6871,21 @@ static void *watchpool_thread(void __maybe_unused *userdata)
 				if (pool_active(pool, true) && pool_tclear(pool, &pool->idle))
 				if (pool_active(pool, true) && pool_tclear(pool, &pool->idle))
 					pool_resus(pool);
 					pool_resus(pool);
 			}
 			}
+
+			/* Only switch pools if the failback pool has been
+			 * alive for more than 5 minutes to prevent
+			 * intermittently failing pools from being used. */
+			if (!pool->idle && pool_strategy == POOL_FAILOVER && pool->prio < cp_prio() &&
+			    now.tv_sec - pool->tv_idle.tv_sec > 300) {
+				applog(LOG_WARNING, "Pool %d %s stable for 5 mins",
+				       pool->pool_no, pool->rpc_url);
+				switch_pools(NULL);
+			}
 		}
 		}
 
 
+		if (current_pool()->idle)
+			switch_pools(NULL);
+
 		if (pool_strategy == POOL_ROTATE && now.tv_sec - rotate_tv.tv_sec > 60 * opt_rotate_period) {
 		if (pool_strategy == POOL_ROTATE && now.tv_sec - rotate_tv.tv_sec > 60 * opt_rotate_period) {
 			cgtime(&rotate_tv);
 			cgtime(&rotate_tv);
 			switch_pools(NULL);
 			switch_pools(NULL);
@@ -7067,6 +7084,9 @@ static void log_print_status(struct cgpu_info *cgpu)
 	applog(LOG_WARNING, "%s", logline);
 	applog(LOG_WARNING, "%s", logline);
 }
 }
 
 
+static void noop_get_statline(char __maybe_unused *buf, size_t __maybe_unused bufsiz, struct cgpu_info __maybe_unused *cgpu);
+void blank_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info __maybe_unused *cgpu);
+
 void print_summary(void)
 void print_summary(void)
 {
 {
 	struct timeval diff;
 	struct timeval diff;
@@ -7138,6 +7158,8 @@ void print_summary(void)
 	for (i = 0; i < total_devices; ++i) {
 	for (i = 0; i < total_devices; ++i) {
 		struct cgpu_info *cgpu = get_devices(i);
 		struct cgpu_info *cgpu = get_devices(i);
 
 
+		cgpu->drv->get_statline_before = &blank_get_statline_before;
+		cgpu->drv->get_statline = &noop_get_statline;
 		log_print_status(cgpu);
 		log_print_status(cgpu);
 	}
 	}
 
 
@@ -7232,6 +7254,7 @@ static void *test_pool_thread(void *arg)
 			applog(LOG_NOTICE, "Switching to pool %d %s - first alive pool", pool->pool_no, pool->rpc_url);
 			applog(LOG_NOTICE, "Switching to pool %d %s - first alive pool", pool->pool_no, pool->rpc_url);
 
 
 		pool_resus(pool);
 		pool_resus(pool);
+		switch_pools(NULL);
 	} else
 	} else
 		pool_died(pool);
 		pool_died(pool);
 
 
@@ -7728,19 +7751,15 @@ static void probe_pools(void)
 #ifdef USE_USBUTILS
 #ifdef USE_USBUTILS
 static void *libusb_poll_thread(void __maybe_unused *arg)
 static void *libusb_poll_thread(void __maybe_unused *arg)
 {
 {
-	struct timeval tv = { 0, USB_ASYNC_POLL * 1000 };
-
 	RenameThread("usbpoll");
 	RenameThread("usbpoll");
 
 
 	pthread_detach(pthread_self());
 	pthread_detach(pthread_self());
-	while (42)
-		libusb_handle_events_timeout(NULL, &tv);
+	while (usb_polling)
+		libusb_handle_events(NULL);
 
 
 	return NULL;
 	return NULL;
 }
 }
 
 
-static pthread_t usb_poll_thread;
-
 static void initialise_usb(void) {
 static void initialise_usb(void) {
 	int err = libusb_init(NULL);
 	int err = libusb_init(NULL);
 	if (err) {
 	if (err) {
@@ -7751,6 +7770,7 @@ static void initialise_usb(void) {
 	mutex_init(&cgusb_lock);
 	mutex_init(&cgusb_lock);
 	mutex_init(&cgusbres_lock);
 	mutex_init(&cgusbres_lock);
 	cglock_init(&cgusb_fd_lock);
 	cglock_init(&cgusb_fd_lock);
+	usb_polling = true;
 	pthread_create(&usb_poll_thread, NULL, libusb_poll_thread, NULL);
 	pthread_create(&usb_poll_thread, NULL, libusb_poll_thread, NULL);
 }
 }
 #else
 #else

+ 1 - 0
driver-avalon.c

@@ -720,6 +720,7 @@ static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found
 	 * all ourselves so set it to std usb type */
 	 * all ourselves so set it to std usb type */
 	avalon->usbdev->usb_type = USB_TYPE_STD;
 	avalon->usbdev->usb_type = USB_TYPE_STD;
 	usb_set_pps(avalon, AVALON_USB_PACKETSIZE);
 	usb_set_pps(avalon, AVALON_USB_PACKETSIZE);
+	usb_buffer_enable(avalon);
 
 
 	/* We have a real Avalon! */
 	/* We have a real Avalon! */
 	avalon_initialise(avalon);
 	avalon_initialise(avalon);

+ 52 - 22
driver-bitfury.c

@@ -15,6 +15,8 @@
 
 
 /* Wait longer 1/3 longer than it would take for a full nonce range */
 /* Wait longer 1/3 longer than it would take for a full nonce range */
 #define BF1WAIT 1600
 #define BF1WAIT 1600
+#define BF1MSGSIZE 7
+#define BF1INFOSIZE 14
 
 
 static void bitfury_empty_buffer(struct cgpu_info *bitfury)
 static void bitfury_empty_buffer(struct cgpu_info *bitfury)
 {
 {
@@ -26,18 +28,29 @@ static void bitfury_empty_buffer(struct cgpu_info *bitfury)
 	} while (amount);
 	} while (amount);
 }
 }
 
 
-static void bitfury_open(struct cgpu_info *bitfury)
+static int bitfury_open(struct cgpu_info *bitfury)
 {
 {
 	uint32_t buf[2];
 	uint32_t buf[2];
+	int err;
 
 
 	bitfury_empty_buffer(bitfury);
 	bitfury_empty_buffer(bitfury);
 	/* Magic sequence to reset device only really needed for windows but
 	/* Magic sequence to reset device only really needed for windows but
 	 * harmless on linux. */
 	 * harmless on linux. */
 	buf[0] = 0x80250000;
 	buf[0] = 0x80250000;
 	buf[1] = 0x00000800;
 	buf[1] = 0x00000800;
-	usb_transfer(bitfury, 0, 9, 1, 0, C_BF1_RESET);
-	usb_transfer(bitfury, 0x21, 0x22, 0, 0, C_BF1_OPEN);
-	usb_transfer_data(bitfury, 0x21, 0x20, 0x0000, 0, buf, 7, C_BF1_INIT);
+	err = usb_transfer(bitfury, 0, 9, 1, 0, C_BF1_RESET);
+	if (!err)
+		err = usb_transfer(bitfury, 0x21, 0x22, 0, 0, C_BF1_OPEN);
+	if (!err) {
+		err = usb_transfer_data(bitfury, 0x21, 0x20, 0x0000, 0, buf,
+					BF1MSGSIZE, C_BF1_INIT);
+	}
+
+	if (err < 0) {
+		applog(LOG_INFO, "%s %d: Failed to open with error %s", bitfury->drv->name,
+		       bitfury->device_id, libusb_error_name(err));
+	}
+	return (err == BF1MSGSIZE);
 }
 }
 
 
 static void bitfury_close(struct cgpu_info *bitfury)
 static void bitfury_close(struct cgpu_info *bitfury)
@@ -63,15 +76,15 @@ static bool bitfury_getinfo(struct cgpu_info *bitfury, struct bitfury_info *info
 		       bitfury->drv->name, bitfury->device_id);
 		       bitfury->drv->name, bitfury->device_id);
 		return false;
 		return false;
 	}
 	}
-	err = usb_read(bitfury, buf, 14, &amount, C_BF1_GETINFO);
+	err = usb_read(bitfury, buf, BF1INFOSIZE, &amount, C_BF1_GETINFO);
 	if (err) {
 	if (err) {
 		applog(LOG_INFO, "%s %d: Failed to read GETINFO",
 		applog(LOG_INFO, "%s %d: Failed to read GETINFO",
 		       bitfury->drv->name, bitfury->device_id);
 		       bitfury->drv->name, bitfury->device_id);
 		return false;
 		return false;
 	}
 	}
-	if (amount != 14) {
-		applog(LOG_INFO, "%s %d: Getinfo received %d bytes instead of 14",
-		       bitfury->drv->name, bitfury->device_id, amount);
+	if (amount != BF1INFOSIZE) {
+		applog(LOG_INFO, "%s %d: Getinfo received %d bytes instead of %d",
+		       bitfury->drv->name, bitfury->device_id, amount, BF1INFOSIZE);
 		return false;
 		return false;
 	}
 	}
 	info->version = buf[1];
 	info->version = buf[1];
@@ -95,15 +108,16 @@ static bool bitfury_reset(struct cgpu_info *bitfury)
 		       bitfury->drv->name, bitfury->device_id);
 		       bitfury->drv->name, bitfury->device_id);
 		return false;
 		return false;
 	}
 	}
-	err = usb_read_timeout(bitfury, buf, 7, &amount, BF1WAIT, C_BF1_GETRESET);
+	err = usb_read_timeout(bitfury, buf, BF1MSGSIZE, &amount, BF1WAIT,
+			       C_BF1_GETRESET);
 	if (err) {
 	if (err) {
 		applog(LOG_INFO, "%s %d: Failed to read GETRESET",
 		applog(LOG_INFO, "%s %d: Failed to read GETRESET",
 		       bitfury->drv->name, bitfury->device_id);
 		       bitfury->drv->name, bitfury->device_id);
 		return false;
 		return false;
 	}
 	}
-	if (amount != 7) {
-		applog(LOG_INFO, "%s %d: Getreset received %d bytes instead of 7",
-		       bitfury->drv->name, bitfury->device_id, amount);
+	if (amount != BF1MSGSIZE) {
+		applog(LOG_INFO, "%s %d: Getreset received %d bytes instead of %d",
+		       bitfury->drv->name, bitfury->device_id, amount, BF1MSGSIZE);
 		return false;
 		return false;
 	}
 	}
 	applog(LOG_DEBUG, "%s %d: Getreset returned %s", bitfury->drv->name,
 	applog(LOG_DEBUG, "%s %d: Getreset returned %s", bitfury->drv->name,
@@ -128,10 +142,14 @@ static bool bitfury_detect_one(struct libusb_device *dev, struct usb_find_device
 	if (!info)
 	if (!info)
 		quit(1, "Failed to calloc info in bitfury_detect_one");
 		quit(1, "Failed to calloc info in bitfury_detect_one");
 	bitfury->device_data = info;
 	bitfury->device_data = info;
+	/* This does not artificially raise hashrate, it simply allows the
+	 * hashrate to adapt quickly on starting. */
+	info->total_nonces = 1;
 
 
 	usb_buffer_enable(bitfury);
 	usb_buffer_enable(bitfury);
 
 
-	bitfury_open(bitfury);
+	if (!bitfury_open(bitfury))
+		goto out_close;
 
 
 	/* Send getinfo request */
 	/* Send getinfo request */
 	if (!bitfury_getinfo(bitfury, info))
 	if (!bitfury_getinfo(bitfury, info))
@@ -145,7 +163,7 @@ static bool bitfury_detect_one(struct libusb_device *dev, struct usb_find_device
 	bitfury_empty_buffer(bitfury);
 	bitfury_empty_buffer(bitfury);
 
 
 	if (!add_cgpu(bitfury))
 	if (!add_cgpu(bitfury))
-		goto out_close;
+		quit(1, "Failed to add_cgpu in bitfury_detect_one");
 
 
 	update_usb_stats(bitfury);
 	update_usb_stats(bitfury);
 	applog(LOG_INFO, "%s %d: Successfully initialised %s",
 	applog(LOG_INFO, "%s %d: Successfully initialised %s",
@@ -208,6 +226,8 @@ static int64_t bitfury_scanhash(struct thr_info *thr, struct work *work,
 	struct cgpu_info *bitfury = thr->cgpu;
 	struct cgpu_info *bitfury = thr->cgpu;
 	struct bitfury_info *info = bitfury->device_data;
 	struct bitfury_info *info = bitfury->device_data;
 	struct timeval tv_now;
 	struct timeval tv_now;
+	double nonce_rate;
+	int64_t ret = 0;
 	int amount, i;
 	int amount, i;
 	char buf[45];
 	char buf[45];
 	int ms_diff;
 	int ms_diff;
@@ -235,7 +255,8 @@ static int64_t bitfury_scanhash(struct thr_info *thr, struct work *work,
 	ms_diff = BF1WAIT - ms_tdiff(&tv_now, &info->tv_start);
 	ms_diff = BF1WAIT - ms_tdiff(&tv_now, &info->tv_start);
 	if (unlikely(ms_diff < 10))
 	if (unlikely(ms_diff < 10))
 		ms_diff = 10;
 		ms_diff = 10;
-	usb_read_once_timeout(bitfury, info->buf + info->tot, 7, &amount, ms_diff, C_BF1_GETRES);
+	usb_read_once_timeout(bitfury, info->buf + info->tot, BF1MSGSIZE,
+			      &amount, ms_diff, C_BF1_GETRES);
 	info->tot += amount;
 	info->tot += amount;
 	while (amount) {
 	while (amount) {
 		usb_read_once_timeout(bitfury, info->buf + info->tot, 512, &amount, 10, C_BF1_GETRES);
 		usb_read_once_timeout(bitfury, info->buf + info->tot, 512, &amount, 10, C_BF1_GETRES);
@@ -249,7 +270,7 @@ static int64_t bitfury_scanhash(struct thr_info *thr, struct work *work,
 	usb_write(bitfury, buf, 45, &amount, C_BF1_REQWORK);
 	usb_write(bitfury, buf, 45, &amount, C_BF1_REQWORK);
 	cgtime(&info->tv_start);
 	cgtime(&info->tv_start);
 	/* Get response acknowledging work */
 	/* Get response acknowledging work */
-	usb_read(bitfury, buf, 7, &amount, C_BF1_GETWORK);
+	usb_read(bitfury, buf, BF1MSGSIZE, &amount, C_BF1_GETWORK);
 
 
 	/* Only happens on startup */
 	/* Only happens on startup */
 	if (unlikely(!info->prevwork[BF1ARRAY_SIZE]))
 	if (unlikely(!info->prevwork[BF1ARRAY_SIZE]))
@@ -257,7 +278,7 @@ static int64_t bitfury_scanhash(struct thr_info *thr, struct work *work,
 
 
 	/* Search for what work the nonce matches in order of likelihood. Last
 	/* Search for what work the nonce matches in order of likelihood. Last
 	 * entry is end of result marker. */
 	 * entry is end of result marker. */
-	for (i = 0; i < info->tot - 7; i += 7) {
+	for (i = 0; i < info->tot - BF1MSGSIZE; i += BF1MSGSIZE) {
 		uint32_t nonce;
 		uint32_t nonce;
 		int j;
 		int j;
 
 
@@ -279,23 +300,30 @@ cascade:
 		info->prevwork[i] = info->prevwork[i - 1];
 		info->prevwork[i] = info->prevwork[i - 1];
 	info->prevwork[0] = copy_work(work);
 	info->prevwork[0] = copy_work(work);
 	work->blk.nonce = 0xffffffff;
 	work->blk.nonce = 0xffffffff;
-	if (info->nonces) {
-		info->nonces--;
-		return (int64_t)0xffffffff;
+
+	info->cycles++;
+	info->total_nonces += info->nonces;
+	info->saved_nonces += info->nonces;
+	info->nonces = 0;
+	nonce_rate = (double)info->total_nonces / (double)info->cycles;
+	if (info->saved_nonces >= nonce_rate) {
+		info->saved_nonces -= nonce_rate;
+		ret = (double)0xffffffff * nonce_rate;
 	}
 	}
 
 
 	if (unlikely(bitfury->usbinfo.nodev)) {
 	if (unlikely(bitfury->usbinfo.nodev)) {
 		applog(LOG_WARNING, "%s %d: Device disappeared, disabling thread",
 		applog(LOG_WARNING, "%s %d: Device disappeared, disabling thread",
 		       bitfury->drv->name, bitfury->device_id);
 		       bitfury->drv->name, bitfury->device_id);
-		return -1;
+		ret = -1;
 	}
 	}
-	return 0;
+	return ret;
 }
 }
 
 
 static struct api_data *bitfury_api_stats(struct cgpu_info *cgpu)
 static struct api_data *bitfury_api_stats(struct cgpu_info *cgpu)
 {
 {
 	struct bitfury_info *info = cgpu->device_data;
 	struct bitfury_info *info = cgpu->device_data;
 	struct api_data *root = NULL;
 	struct api_data *root = NULL;
+	double nonce_rate;
 	char serial[16];
 	char serial[16];
 	int version;
 	int version;
 
 
@@ -304,6 +332,8 @@ static struct api_data *bitfury_api_stats(struct cgpu_info *cgpu)
 	root = api_add_string(root, "Product", info->product, false);
 	root = api_add_string(root, "Product", info->product, false);
 	sprintf(serial, "%08x", info->serial);
 	sprintf(serial, "%08x", info->serial);
 	root = api_add_string(root, "Serial", serial, true);
 	root = api_add_string(root, "Serial", serial, true);
+	nonce_rate = (double)info->total_nonces / (double)info->cycles;
+	root = api_add_double(root, "NonceRate", &nonce_rate, true);
 
 
 	return root;
 	return root;
 }
 }

+ 3 - 0
driver-bitfury.h

@@ -24,6 +24,9 @@ struct bitfury_info {
 	char buf[512];
 	char buf[512];
 	int tot;
 	int tot;
 	int nonces;
 	int nonces;
+	int total_nonces;
+	double saved_nonces;
+	int cycles;
 	struct timeval tv_start;
 	struct timeval tv_start;
 };
 };
 
 

+ 217 - 22
driver-icarus.c

@@ -108,6 +108,8 @@ ASSERT1(sizeof(uint32_t) == 4);
 #define CAIRNSMORE2_HASH_TIME 0.0000000066600
 #define CAIRNSMORE2_HASH_TIME 0.0000000066600
 #define NANOSEC 1000000000.0
 #define NANOSEC 1000000000.0
 
 
+#define CAIRNSMORE2_INTS 4
+
 // Icarus Rev3 doesn't send a completion message when it finishes
 // Icarus Rev3 doesn't send a completion message when it finishes
 // the full nonce range, so to avoid being idle we must abort the
 // the full nonce range, so to avoid being idle we must abort the
 // work (by starting a new work item) shortly before it finishes
 // work (by starting a new work item) shortly before it finishes
@@ -178,6 +180,7 @@ static const char *MODE_VALUE_STR = "value";
 static const char *MODE_UNKNOWN_STR = "unknown";
 static const char *MODE_UNKNOWN_STR = "unknown";
 
 
 struct ICARUS_INFO {
 struct ICARUS_INFO {
+	enum sub_ident ident;
 	int intinfo;
 	int intinfo;
 
 
 	// time to calculate the golden_ob
 	// time to calculate the golden_ob
@@ -216,17 +219,43 @@ struct ICARUS_INFO {
 	int fpga_count;
 	int fpga_count;
 	uint32_t nonce_mask;
 	uint32_t nonce_mask;
 
 
-	bool initialised;
+	uint8_t cmr2_speed;
+	bool speed_next_work;
+	bool flash_next_work;
 };
 };
 
 
 #define ICARUS_MIDSTATE_SIZE 32
 #define ICARUS_MIDSTATE_SIZE 32
-#define ICARUS_UNUSED_SIZE 20
+#define ICARUS_UNUSED_SIZE 16
 #define ICARUS_WORK_SIZE 12
 #define ICARUS_WORK_SIZE 12
 
 
 #define ICARUS_WORK_DATA_OFFSET 64
 #define ICARUS_WORK_DATA_OFFSET 64
 
 
+#define ICARUS_CMR2_SPEED_FACTOR 2.5
+#define ICARUS_CMR2_SPEED_MIN_INT 100
+#define ICARUS_CMR2_SPEED_DEF_INT 180
+#define ICARUS_CMR2_SPEED_MAX_INT 220
+#define CMR2_INT_TO_SPEED(_speed) ((uint8_t)((float)_speed / ICARUS_CMR2_SPEED_FACTOR))
+#define ICARUS_CMR2_SPEED_MIN CMR2_INT_TO_SPEED(ICARUS_CMR2_SPEED_MIN_INT)
+#define ICARUS_CMR2_SPEED_DEF CMR2_INT_TO_SPEED(ICARUS_CMR2_SPEED_DEF_INT)
+#define ICARUS_CMR2_SPEED_MAX CMR2_INT_TO_SPEED(ICARUS_CMR2_SPEED_MAX_INT)
+#define ICARUS_CMR2_SPEED_INC 1
+#define ICARUS_CMR2_SPEED_DEC -1
+#define ICARUS_CMR2_SPEED_FAIL -10
+
+#define ICARUS_CMR2_PREFIX ((uint8_t)0xB7)
+#define ICARUS_CMR2_CMD_SPEED ((uint8_t)0)
+#define ICARUS_CMR2_CMD_FLASH ((uint8_t)1)
+#define ICARUS_CMR2_DATA_FLASH_OFF ((uint8_t)0)
+#define ICARUS_CMR2_DATA_FLASH_ON ((uint8_t)1)
+#define ICARUS_CMR2_CHECK ((uint8_t)0x6D)
+
 struct ICARUS_WORK {
 struct ICARUS_WORK {
 	uint8_t midstate[ICARUS_MIDSTATE_SIZE];
 	uint8_t midstate[ICARUS_MIDSTATE_SIZE];
+	// These 4 bytes are for CMR2 bitstreams that handle MHz adjustment
+	uint8_t check;
+	uint8_t data;
+	uint8_t cmd;
+	uint8_t prefix;
 	uint8_t unused[ICARUS_UNUSED_SIZE];
 	uint8_t unused[ICARUS_UNUSED_SIZE];
 	uint8_t work[ICARUS_WORK_SIZE];
 	uint8_t work[ICARUS_WORK_SIZE];
 };
 };
@@ -423,8 +452,6 @@ static void icarus_initialise(struct cgpu_info *icarus, int baud)
 			quit(1, "icarus_intialise() called with invalid %s cgid %i ident=%d",
 			quit(1, "icarus_intialise() called with invalid %s cgid %i ident=%d",
 				icarus->drv->name, icarus->cgminer_id, ident);
 				icarus->drv->name, icarus->cgminer_id, ident);
 	}
 	}
-
-	info->initialised = true;
 }
 }
 
 
 static void rev(unsigned char *s, size_t l)
 static void rev(unsigned char *s, size_t l)
@@ -820,9 +847,10 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 	char *nonce_hex;
 	char *nonce_hex;
 	int baud, uninitialised_var(work_division), uninitialised_var(fpga_count);
 	int baud, uninitialised_var(work_division), uninitialised_var(fpga_count);
 	struct cgpu_info *icarus;
 	struct cgpu_info *icarus;
-	int ret, err, amount, tries;
-	enum sub_ident ident;
+	int ret, err, amount, tries, i;
 	bool ok;
 	bool ok;
+	bool cmr2_ok[CAIRNSMORE2_INTS];
+	int cmr2_count;
 
 
 	if ((sizeof(workdata) << 1) != (sizeof(golden_ob) - 1))
 	if ((sizeof(workdata) << 1) != (sizeof(golden_ob) - 1))
 		quithere(1, "Data and golden_ob sizes don't match");
 		quithere(1, "Data and golden_ob sizes don't match");
@@ -843,8 +871,8 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 		quit(1, "Failed to malloc ICARUS_INFO");
 		quit(1, "Failed to malloc ICARUS_INFO");
 	icarus->device_data = (void *)info;
 	icarus->device_data = (void *)info;
 
 
-	ident = usb_ident(icarus);
-	switch (ident) {
+	info->ident = usb_ident(icarus);
+	switch (info->ident) {
 		case IDENT_ICA:
 		case IDENT_ICA:
 		case IDENT_BLT:
 		case IDENT_BLT:
 		case IDENT_LLT:
 		case IDENT_LLT:
@@ -853,19 +881,32 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 			info->timeout = ICARUS_WAIT_TIMEOUT;
 			info->timeout = ICARUS_WAIT_TIMEOUT;
 			break;
 			break;
 		case IDENT_CMR2:
 		case IDENT_CMR2:
+			if (found->intinfo_count != CAIRNSMORE2_INTS) {
+				quithere(1, "CMR2 Interface count (%d) isn't expected: %d",
+						found->intinfo_count,
+						CAIRNSMORE2_INTS);
+			}
 			info->timeout = ICARUS_CMR2_TIMEOUT;
 			info->timeout = ICARUS_CMR2_TIMEOUT;
+			cmr2_count = 0;
+			for (i = 0; i < CAIRNSMORE2_INTS; i++)
+				cmr2_ok[i] = false;
 			break;
 			break;
 		default:
 		default:
 			quit(1, "%s icarus_detect_one() invalid %s ident=%d",
 			quit(1, "%s icarus_detect_one() invalid %s ident=%d",
-				icarus->drv->dname, icarus->drv->dname, ident);
+				icarus->drv->dname, icarus->drv->dname, info->ident);
 	}
 	}
 
 
+// For CMR2 test each USB Interface
+
+cmr2_retry:
+
 	tries = 2;
 	tries = 2;
 	ok = false;
 	ok = false;
 	while (!ok && tries-- > 0) {
 	while (!ok && tries-- > 0) {
 		icarus_initialise(icarus, baud);
 		icarus_initialise(icarus, baud);
 
 
-		err = usb_write(icarus, (void *)(&workdata), sizeof(workdata), &amount, C_SENDTESTWORK);
+		err = usb_write_ii(icarus, info->intinfo,
+				   (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK);
 
 
 		if (err != LIBUSB_SUCCESS || amount != sizeof(workdata))
 		if (err != LIBUSB_SUCCESS || amount != sizeof(workdata))
 			continue;
 			continue;
@@ -879,7 +920,7 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 		if (strncmp(nonce_hex, golden_nonce, 8) == 0)
 		if (strncmp(nonce_hex, golden_nonce, 8) == 0)
 			ok = true;
 			ok = true;
 		else {
 		else {
-			if (tries < 0) {
+			if (tries < 0 && info->ident != IDENT_CMR2) {
 				applog(LOG_ERR,
 				applog(LOG_ERR,
 					"Icarus Detect: "
 					"Icarus Detect: "
 					"Test failed at %s: get %s, should: %s",
 					"Test failed at %s: get %s, should: %s",
@@ -889,13 +930,50 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 		free(nonce_hex);
 		free(nonce_hex);
 	}
 	}
 
 
-	if (!ok)
-		goto unshin;
+	if (!ok) {
+		if (info->ident != IDENT_CMR2)
+			goto unshin;
+
+		if (info->intinfo < CAIRNSMORE2_INTS-1) {
+			info->intinfo++;
+			goto cmr2_retry;
+		}
+	} else {
+		if (info->ident == IDENT_CMR2) {
+			applog(LOG_DEBUG,
+				"Icarus Detect: "
+				"Test succeeded at %s i%d: got %s",
+					icarus->device_path, info->intinfo, golden_nonce);
+
+			cmr2_ok[info->intinfo] = true;
+			cmr2_count++;
+			if (info->intinfo < CAIRNSMORE2_INTS-1) {
+				info->intinfo++;
+				goto cmr2_retry;
+			}
+		}
+	}
 
 
-	applog(LOG_DEBUG,
-		"Icarus Detect: "
-		"Test succeeded at %s: got %s",
-			icarus->device_path, golden_nonce);
+	if (info->ident == IDENT_CMR2) {
+		if (cmr2_count == 0) {
+			applog(LOG_ERR,
+				"Icarus Detect: Test failed at %s: for all %d CMR2 Interfaces",
+				icarus->device_path, CAIRNSMORE2_INTS);
+			goto unshin;
+		}
+
+		// set the interface to the first one that succeeded
+		for (i = 0; i < CAIRNSMORE2_INTS; i++)
+			if (cmr2_ok[i]) {
+				info->intinfo = i;
+				break;
+			}
+	} else {
+		applog(LOG_DEBUG,
+			"Icarus Detect: "
+			"Test succeeded at %s: got %s",
+				icarus->device_path, golden_nonce);
+	}
 
 
 	/* We have a real Icarus! */
 	/* We have a real Icarus! */
 	if (!add_cgpu(icarus))
 	if (!add_cgpu(icarus))
@@ -906,6 +984,18 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 	applog(LOG_INFO, "%s%d: Found at %s",
 	applog(LOG_INFO, "%s%d: Found at %s",
 		icarus->drv->name, icarus->device_id, icarus->device_path);
 		icarus->drv->name, icarus->device_id, icarus->device_path);
 
 
+	if (info->ident == IDENT_CMR2) {
+		applog(LOG_INFO, "%s%d: with %d Interface%s",
+				icarus->drv->name, icarus->device_id,
+				cmr2_count, cmr2_count > 1 ? "s" : "");
+
+		// Assume 1 or 2 are running FPGA pairs
+		if (cmr2_count < 3) {
+			work_division = fpga_count = 2;
+			info->Hs /= 2;
+		}
+	}
+
 	applog(LOG_DEBUG, "%s%d: Init baud=%d work_division=%d fpga_count=%d",
 	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);
 		icarus->drv->name, icarus->device_id, baud, work_division, fpga_count);
 
 
@@ -919,12 +1009,15 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 
 
 	set_timing_mode(this_option_offset, icarus);
 	set_timing_mode(this_option_offset, icarus);
 	
 	
-	if (usb_ident(icarus) == IDENT_CMR2) {
+	if (info->ident == IDENT_CMR2) {
 		int i;
 		int i;
-		for (i = 1; i < icarus->usbdev->found->intinfo_count; i++) {
+		for (i = info->intinfo + 1; i < icarus->usbdev->found->intinfo_count; i++) {
 			struct cgpu_info *cgtmp;
 			struct cgpu_info *cgtmp;
 			struct ICARUS_INFO *intmp;
 			struct ICARUS_INFO *intmp;
 
 
+			if (!cmr2_ok[i])
+				continue;
+
 			cgtmp = usb_copy_cgpu(icarus);
 			cgtmp = usb_copy_cgpu(icarus);
 			if (!cgtmp) {
 			if (!cgtmp) {
 				applog(LOG_ERR, "%s%d: Init failed initinfo %d",
 				applog(LOG_ERR, "%s%d: Init failed initinfo %d",
@@ -984,6 +1077,45 @@ static bool icarus_prepare(__maybe_unused struct thr_info *thr)
 	return true;
 	return true;
 }
 }
 
 
+static void cmr2_command(struct cgpu_info *icarus, uint8_t cmd, uint8_t data)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data);
+	struct ICARUS_WORK workdata;
+	int amount;
+
+	memset((void *)(&workdata), 0, sizeof(workdata));
+
+	workdata.prefix = ICARUS_CMR2_PREFIX;
+	workdata.cmd = cmd;
+	workdata.data = data;
+	workdata.check = workdata.data ^ workdata.cmd ^ workdata.prefix ^ ICARUS_CMR2_CHECK;
+
+	usb_write_ii(icarus, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK);
+}
+
+static void cmr2_commands(struct cgpu_info *icarus)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data);
+
+	if (info->speed_next_work) {
+		info->speed_next_work = false;
+		cmr2_command(icarus, ICARUS_CMR2_CMD_SPEED, info->cmr2_speed);
+		return;
+	}
+
+	if (info->flash_next_work) {
+		info->flash_next_work = false;
+		cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_ON);
+		cgsleep_ms(250);
+		cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_OFF);
+		cgsleep_ms(250);
+		cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_ON);
+		cgsleep_ms(250);
+		cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_OFF);
+		return;
+	}
+}
+
 static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 				__maybe_unused int64_t max_nonce)
 				__maybe_unused int64_t max_nonce)
 {
 {
@@ -1014,9 +1146,6 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	if (icarus->usbinfo.nodev)
 	if (icarus->usbinfo.nodev)
 		return -1;
 		return -1;
 
 
-	if (!info->initialised)
-		icarus_initialise(icarus, info->baud);
-
 	elapsed.tv_sec = elapsed.tv_usec = 0;
 	elapsed.tv_sec = elapsed.tv_usec = 0;
 
 
 	memset((void *)(&workdata), 0, sizeof(workdata));
 	memset((void *)(&workdata), 0, sizeof(workdata));
@@ -1025,6 +1154,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	rev((void *)(&(workdata.midstate)), ICARUS_MIDSTATE_SIZE);
 	rev((void *)(&(workdata.midstate)), ICARUS_MIDSTATE_SIZE);
 	rev((void *)(&(workdata.work)), ICARUS_WORK_SIZE);
 	rev((void *)(&(workdata.work)), ICARUS_WORK_SIZE);
 
 
+	if (info->speed_next_work || info->flash_next_work)
+		cmr2_commands(icarus);
+
 	// We only want results for the work we are about to send
 	// We only want results for the work we are about to send
 	usb_buffer_clear(icarus);
 	usb_buffer_clear(icarus);
 
 
@@ -1245,17 +1377,80 @@ static struct api_data *icarus_api_stats(struct cgpu_info *cgpu)
 	return root;
 	return root;
 }
 }
 
 
+static void icarus_statline_before(char *buf, size_t bufsiz, struct cgpu_info *cgpu)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data);
+
+	if (info->ident == IDENT_CMR2 && info->cmr2_speed > 0)
+		tailsprintf(buf, bufsiz, "%5.1fMhz", (float)(info->cmr2_speed) * ICARUS_CMR2_SPEED_FACTOR);
+	else
+		tailsprintf(buf, bufsiz, "        ");
+
+	tailsprintf(buf, bufsiz, "        | ");
+}
+
 static void icarus_shutdown(__maybe_unused struct thr_info *thr)
 static void icarus_shutdown(__maybe_unused struct thr_info *thr)
 {
 {
 	// TODO: ?
 	// TODO: ?
 }
 }
 
 
+static void icarus_identify(struct cgpu_info *cgpu)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data);
+
+	if (info->ident == IDENT_CMR2)
+		info->flash_next_work = true;
+}
+
+static char *icarus_set(struct cgpu_info *cgpu, char *option, char *setting, char *replybuf)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data);
+	int val;
+
+	if (info->ident != IDENT_CMR2) {
+		strcpy(replybuf, "no set options available");
+		return replybuf;
+	}
+
+	if (strcasecmp(option, "help") == 0) {
+		sprintf(replybuf, "clock: range %d-%d",
+				  ICARUS_CMR2_SPEED_MIN_INT, ICARUS_CMR2_SPEED_MAX_INT);
+		return replybuf;
+	}
+
+	if (strcasecmp(option, "clock") == 0) {
+		if (!setting || !*setting) {
+			sprintf(replybuf, "missing clock setting");
+			return replybuf;
+		}
+
+		val = atoi(setting);
+		if (val < ICARUS_CMR2_SPEED_MIN_INT || val > ICARUS_CMR2_SPEED_MAX_INT) {
+			sprintf(replybuf, "invalid clock: '%s' valid range %d-%d",
+					  setting,
+					  ICARUS_CMR2_SPEED_MIN_INT,
+					  ICARUS_CMR2_SPEED_MAX_INT);
+		}
+
+		info->cmr2_speed = CMR2_INT_TO_SPEED(val);
+		info->speed_next_work = true;
+
+		return NULL;
+	}
+
+	sprintf(replybuf, "Unknown option: %s", option);
+	return replybuf;
+}
+
 struct device_drv icarus_drv = {
 struct device_drv icarus_drv = {
 	.drv_id = DRIVER_icarus,
 	.drv_id = DRIVER_icarus,
 	.dname = "Icarus",
 	.dname = "Icarus",
 	.name = "ICA",
 	.name = "ICA",
 	.drv_detect = icarus_detect,
 	.drv_detect = icarus_detect,
 	.get_api_stats = icarus_api_stats,
 	.get_api_stats = icarus_api_stats,
+	.get_statline_before = icarus_statline_before,
+	.set_device = icarus_set,
+	.identify_device = icarus_identify,
 	.thread_prepare = icarus_prepare,
 	.thread_prepare = icarus_prepare,
 	.scanhash = icarus_scanhash,
 	.scanhash = icarus_scanhash,
 	.thread_shutdown = icarus_shutdown,
 	.thread_shutdown = icarus_shutdown,

+ 8 - 1
miner.h

@@ -472,7 +472,6 @@ struct cgpu_info {
 #endif
 #endif
 #ifdef USE_USBUTILS
 #ifdef USE_USBUTILS
 	struct cg_usb_info usbinfo;
 	struct cg_usb_info usbinfo;
-	int usb_cancels;
 #endif
 #endif
 #ifdef USE_MODMINER
 #ifdef USE_MODMINER
 	char fpgaid;
 	char fpgaid;
@@ -881,6 +880,14 @@ static inline void cg_runlock(cglock_t *lock)
 	rd_unlock(&lock->rwlock);
 	rd_unlock(&lock->rwlock);
 }
 }
 
 
+/* This drops the read lock and grabs a write lock. It does NOT protect data
+ * between the two locks! */
+static inline void cg_ruwlock(cglock_t *lock)
+{
+	rd_unlock_noyield(&lock->rwlock);
+	cg_wlock(lock);
+}
+
 static inline void cg_wunlock(cglock_t *lock)
 static inline void cg_wunlock(cglock_t *lock)
 {
 {
 	wr_unlock_noyield(&lock->rwlock);
 	wr_unlock_noyield(&lock->rwlock);

+ 54 - 49
usbutils.c

@@ -1300,12 +1300,11 @@ static void release_cgpu(struct cgpu_info *cgpu)
 {
 {
 	struct cg_usb_device *cgusb = cgpu->usbdev;
 	struct cg_usb_device *cgusb = cgpu->usbdev;
 	struct cgpu_info *lookcgpu;
 	struct cgpu_info *lookcgpu;
-	int i, pstate;
+	int i;
 
 
-	DEVWLOCK(cgpu, pstate);
 	// It has already been done
 	// It has already been done
 	if (cgpu->usbinfo.nodev)
 	if (cgpu->usbinfo.nodev)
-		goto out_unlock;
+		return;
 
 
 	applog(LOG_DEBUG, "USB release %s%i",
 	applog(LOG_DEBUG, "USB release %s%i",
 			cgpu->drv->name, cgpu->device_id);
 			cgpu->drv->name, cgpu->device_id);
@@ -1335,8 +1334,6 @@ static void release_cgpu(struct cgpu_info *cgpu)
 
 
 	_usb_uninit(cgpu);
 	_usb_uninit(cgpu);
 	cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address);
 	cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address);
-out_unlock:
-	DEVWUNLOCK(cgpu, pstate);
 }
 }
 
 
 /*
 /*
@@ -2125,7 +2122,6 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev
 		case LIBUSB_SUCCESS:
 		case LIBUSB_SUCCESS:
 			item = CMD_CMD;
 			item = CMD_CMD;
 			break;
 			break;
-		case LIBUSB_TRANSFER_TIMED_OUT:
 		case LIBUSB_ERROR_TIMEOUT:
 		case LIBUSB_ERROR_TIMEOUT:
 			item = CMD_TIMEOUT;
 			item = CMD_TIMEOUT;
 			break;
 			break;
@@ -2222,8 +2218,7 @@ static void LIBUSB_CALL bulk_callback(struct libusb_transfer *transfer)
 
 
 /* Wait for callback function to tell us it has finished the USB transfer, but
 /* Wait for callback function to tell us it has finished the USB transfer, but
  * use our own timer to cancel the request if we go beyond the timeout. */
  * use our own timer to cancel the request if we go beyond the timeout. */
-static int callback_wait(struct cgpu_info *cgpu, struct usb_transfer *ut, int *transferred,
-			 unsigned int timeout)
+static int callback_wait(struct usb_transfer *ut, int *transferred, unsigned int timeout)
 {
 {
 	struct libusb_transfer *transfer= ut->transfer;
 	struct libusb_transfer *transfer= ut->transfer;
 	struct timespec ts_now, ts_end;
 	struct timespec ts_now, ts_end;
@@ -2231,28 +2226,21 @@ static int callback_wait(struct cgpu_info *cgpu, struct usb_transfer *ut, int *t
 	int ret;
 	int ret;
 
 
 	cgtime(&tv_now);
 	cgtime(&tv_now);
-	timeout = timeout + USB_ASYNC_POLL;
 	ms_to_timespec(&ts_end, timeout);
 	ms_to_timespec(&ts_end, timeout);
 	timeval_to_spec(&ts_now, &tv_now);
 	timeval_to_spec(&ts_now, &tv_now);
 	timeraddspec(&ts_end, &ts_now);
 	timeraddspec(&ts_end, &ts_now);
 	ret = pthread_cond_timedwait(&ut->cond, &ut->mutex, &ts_end);
 	ret = pthread_cond_timedwait(&ut->cond, &ut->mutex, &ts_end);
 	if (ret) {
 	if (ret) {
-		/* Assume that if we timed out on the conditional then the
-		 * transfer has stalled for some reason and attempt to clear
-		 * a halt as a solution. Then cancel the transaction, treating
-		 * it the same as a timeout. */
-		libusb_clear_halt(transfer->dev_handle, transfer->endpoint);
+		/* We are emulating a timeout ourself here */
 		libusb_cancel_transfer(transfer);
 		libusb_cancel_transfer(transfer);
-		applog(LOG_DEBUG, "%s%i: libusb cancelling async bulk transfer",
-		       cgpu->drv->name, cgpu->device_id);
-		cgpu->usb_cancels++;
 
 
 		/* Now wait for the callback function to be invoked. */
 		/* Now wait for the callback function to be invoked. */
 		pthread_cond_wait(&ut->cond, &ut->mutex);
 		pthread_cond_wait(&ut->cond, &ut->mutex);
-		/* Fake the timed out message since it's effectively that */
+	}
+	ret = transfer->status;
+	if (ret == LIBUSB_TRANSFER_CANCELLED)
 		ret = LIBUSB_TRANSFER_TIMED_OUT;
 		ret = LIBUSB_TRANSFER_TIMED_OUT;
-	} else
-		ret = transfer->status;
+
 	/* No need to sort out mutexes here since they won't be reused */
 	/* No need to sort out mutexes here since they won't be reused */
 	*transferred = transfer->actual_length;
 	*transferred = transfer->actual_length;
 	libusb_free_transfer(transfer);
 	libusb_free_transfer(transfer);
@@ -2295,8 +2283,9 @@ usb_bulk_transfer(struct libusb_device_handle *dev_handle, int intinfo,
 
 
 	init_usb_transfer(&ut);
 	init_usb_transfer(&ut);
 	mutex_lock(&ut.mutex);
 	mutex_lock(&ut.mutex);
+	/* We give the transfer no timeout since we manage timeouts ourself */
 	libusb_fill_bulk_transfer(ut.transfer, dev_handle, endpoint, buf, length,
 	libusb_fill_bulk_transfer(ut.transfer, dev_handle, endpoint, buf, length,
-				  bulk_callback, &ut, timeout);
+				  bulk_callback, &ut, 0);
 
 
 	STATS_TIMEVAL(&tv_start);
 	STATS_TIMEVAL(&tv_start);
 	cg_rlock(&cgusb_fd_lock);
 	cg_rlock(&cgusb_fd_lock);
@@ -2304,7 +2293,7 @@ usb_bulk_transfer(struct libusb_device_handle *dev_handle, int intinfo,
 	cg_runlock(&cgusb_fd_lock);
 	cg_runlock(&cgusb_fd_lock);
 	errn = errno;
 	errn = errno;
 	if (!err)
 	if (!err)
-		err = callback_wait(cgpu, &ut, transferred, timeout);
+		err = callback_wait(&ut, transferred, timeout);
 
 
 	STATS_TIMEVAL(&tv_finish);
 	STATS_TIMEVAL(&tv_finish);
 	USB_STATS(cgpu, &tv_start, &tv_finish, err, mode, cmd, seq, timeout);
 	USB_STATS(cgpu, &tv_start, &tv_finish, err, mode, cmd, seq, timeout);
@@ -2315,16 +2304,20 @@ usb_bulk_transfer(struct libusb_device_handle *dev_handle, int intinfo,
 				usb_cmdname(cmd), *transferred, err, errn);
 				usb_cmdname(cmd), *transferred, err, errn);
 
 
 	if (err == LIBUSB_ERROR_PIPE || err == LIBUSB_TRANSFER_STALL) {
 	if (err == LIBUSB_ERROR_PIPE || err == LIBUSB_TRANSFER_STALL) {
-		cgpu->usbinfo.last_pipe = time(NULL);
-		cgpu->usbinfo.pipe_count++;
-		applog(LOG_INFO, "%s%i: libusb pipe error, trying to clear",
-			cgpu->drv->name, cgpu->device_id);
-		err = libusb_clear_halt(dev_handle, endpoint);
-		applog(LOG_DEBUG, "%s%i: libusb pipe error%scleared",
-			cgpu->drv->name, cgpu->device_id, err ? " not " : " ");
+		int retries = 0;
 
 
-		if (err)
-			cgpu->usbinfo.clear_fail_count++;
+		do {
+			cgpu->usbinfo.last_pipe = time(NULL);
+			cgpu->usbinfo.pipe_count++;
+			applog(LOG_INFO, "%s%i: libusb pipe error, trying to clear",
+				cgpu->drv->name, cgpu->device_id);
+			err = libusb_clear_halt(dev_handle, endpoint);
+			applog(LOG_DEBUG, "%s%i: libusb pipe error%scleared",
+				cgpu->drv->name, cgpu->device_id, err ? " not " : " ");
+
+			if (err)
+				cgpu->usbinfo.clear_fail_count++;
+		} while (err && ++retries < USB_RETRY_MAX);
 	}
 	}
 	if ((endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
 	if ((endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
 		memcpy(data, buf, length);
 		memcpy(data, buf, length);
@@ -2599,10 +2592,12 @@ out_unlock:
 			err = LIBUSB_ERROR_OTHER;
 			err = LIBUSB_ERROR_OTHER;
 	}
 	}
 out_noerrmsg:
 out_noerrmsg:
-	DEVRUNLOCK(cgpu, pstate);
-
-	if (NODEV(err))
+	if (NODEV(err)) {
+		cg_ruwlock(&cgpu->usbinfo.devlock);
 		release_cgpu(cgpu);
 		release_cgpu(cgpu);
+		DEVWUNLOCK(cgpu, pstate);
+	} else
+		DEVRUNLOCK(cgpu, pstate);
 
 
 	return err;
 	return err;
 }
 }
@@ -2697,10 +2692,12 @@ int _usb_write(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_
 			err = LIBUSB_ERROR_OTHER;
 			err = LIBUSB_ERROR_OTHER;
 	}
 	}
 out_noerrmsg:
 out_noerrmsg:
-	DEVRUNLOCK(cgpu, pstate);
-
-	if (NODEV(err))
+	if (NODEV(err)) {
+		cg_ruwlock(&cgpu->usbinfo.devlock);
 		release_cgpu(cgpu);
 		release_cgpu(cgpu);
+		DEVWUNLOCK(cgpu, pstate);
+	} else
+		DEVRUNLOCK(cgpu, pstate);
 
 
 	return err;
 	return err;
 }
 }
@@ -2772,8 +2769,8 @@ int __usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bReques
 	IOERR_CHECK(cgpu, err);
 	IOERR_CHECK(cgpu, err);
 
 
 	if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) {
 	if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) {
-		applog(LOG_WARNING, "%s %i usb transfer error: %s", cgpu->drv->name, cgpu->device_id,
-		       libusb_error_name(err));
+		applog(LOG_WARNING, "%s %i usb transfer error(%d): %s", cgpu->drv->name, cgpu->device_id,
+		       err, libusb_error_name(err));
 	}
 	}
 out_:
 out_:
 	return err;
 	return err;
@@ -2787,10 +2784,13 @@ int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest
 
 
 	err = __usb_transfer(cgpu, request_type, bRequest, wValue, wIndex, data, siz, timeout, cmd);
 	err = __usb_transfer(cgpu, request_type, bRequest, wValue, wIndex, data, siz, timeout, cmd);
 
 
-	DEVRUNLOCK(cgpu, pstate);
-
-	if (NOCONTROLDEV(err))
+	if (NOCONTROLDEV(err)) {
+		cg_ruwlock(&cgpu->usbinfo.devlock);
 		release_cgpu(cgpu);
 		release_cgpu(cgpu);
+		cg_dwlock(&cgpu->usbinfo.devlock);
+	}
+
+	DEVRUNLOCK(cgpu, pstate);
 
 
 	return err;
 	return err;
 }
 }
@@ -2856,14 +2856,17 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe
 		err = 0;
 		err = 0;
 	}
 	}
 	if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) {
 	if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) {
-		applog(LOG_WARNING, "%s %i usb transfer read error: %s", cgpu->drv->name, cgpu->device_id,
-		       libusb_error_name(err));
+		applog(LOG_WARNING, "%s %i usb transfer read error(%d): %s", cgpu->drv->name, cgpu->device_id,
+		       err, libusb_error_name(err));
 	}
 	}
 out_noerrmsg:
 out_noerrmsg:
-	DEVRUNLOCK(cgpu, pstate);
-
-	if (NOCONTROLDEV(err))
+	if (NOCONTROLDEV(err)) {
+		cg_ruwlock(&cgpu->usbinfo.devlock);
 		release_cgpu(cgpu);
 		release_cgpu(cgpu);
+		cg_dwlock(&cgpu->usbinfo.devlock);
+	}
+
+	DEVRUNLOCK(cgpu, pstate);
 
 
 	return err;
 	return err;
 }
 }
@@ -3129,7 +3132,7 @@ void usb_set_dev_start(struct cgpu_info *cgpu)
 void usb_cleanup(void)
 void usb_cleanup(void)
 {
 {
 	struct cgpu_info *cgpu;
 	struct cgpu_info *cgpu;
-	int count;
+	int count, pstate;
 	int i;
 	int i;
 
 
 	hotplug_time = 0;
 	hotplug_time = 0;
@@ -3147,7 +3150,9 @@ void usb_cleanup(void)
 			case DRIVER_icarus:
 			case DRIVER_icarus:
 			case DRIVER_avalon:
 			case DRIVER_avalon:
 			case DRIVER_klondike:
 			case DRIVER_klondike:
+				DEVWLOCK(cgpu, pstate);
 				release_cgpu(cgpu);
 				release_cgpu(cgpu);
+				DEVWUNLOCK(cgpu, pstate);
 				count++;
 				count++;
 				break;
 				break;
 			default:
 			default:
@@ -3186,7 +3191,7 @@ void usb_cleanup(void)
 	cgsem_destroy(&usb_resource_sem);
 	cgsem_destroy(&usb_resource_sem);
 }
 }
 
 
-#define DRIVER_COUNT_FOUND(X) if (strcasecmp(ptr, X##_drv.name) == 0) { \
+#define DRIVER_COUNT_FOUND(X) if (X##_drv.name && strcasecmp(ptr, X##_drv.name) == 0) { \
 	drv_count[X##_drv.drv_id].limit = lim; \
 	drv_count[X##_drv.drv_id].limit = lim; \
 	found = true; \
 	found = true; \
 	}
 	}

+ 1 - 1
util.c

@@ -2006,7 +2006,7 @@ static bool setup_stratum_socket(struct pool *pool)
 		break;
 		break;
 	}
 	}
 	if (p == NULL) {
 	if (p == NULL) {
-		applog(LOG_NOTICE, "Failed to connect to stratum on %s:%s",
+		applog(LOG_INFO, "Failed to connect to stratum on %s:%s",
 		       sockaddr_url, sockaddr_port);
 		       sockaddr_url, sockaddr_port);
 		freeaddrinfo(servinfo);
 		freeaddrinfo(servinfo);
 		return false;
 		return false;