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
                               stating the results of the identify request
                               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
                               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
-                              support it
+                              support it. Non-CMR ICAs will ignore the command.
                               This adds a 4s delay to the BFL share being
                               processed so you may get a message stating that
                               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:
                                MMQ opt=clock val=160 to 230 (a multiple of 2)
+                               CMR opt=clock val=100 to 220
 
  zero|Which,true/false (*)
                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
 
                               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
 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
 
 - 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_int(root, "USB cancellations", &cgpu->usb_cancels, false);
 #endif
 	}
 

+ 31 - 11
cgminer.c

@@ -175,6 +175,8 @@ char *opt_usb_select = NULL;
 int opt_usbdump = -1;
 bool opt_usb_list_all;
 cgsem_t usb_resource_sem;
+static pthread_t usb_poll_thread;
+static bool usb_polling;
 #endif
 
 char *opt_kernel_path;
@@ -3216,6 +3218,8 @@ static void __kill_work(void)
 	/* Release USB resources in case it's a restart
 	 * and not a QUIT */
 	if (!opt_scrypt) {
+		usb_polling = false;
+
 		applog(LOG_DEBUG, "Releasing all USB devices");
 		usb_cleanup();
 
@@ -5786,10 +5790,9 @@ out:
 
 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);
 }
 
@@ -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);
 				cgpu->deven = DEV_DISABLED;
 				dev_error(cgpu, REASON_THREAD_ZERO_HASH);
-				mt_disable(mythr, thr_id, drv);
+				cgpu->shutdown = true;
+				break;
 			}
 
 			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))
 					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) {
 			cgtime(&rotate_tv);
 			switch_pools(NULL);
@@ -7067,6 +7084,9 @@ static void log_print_status(struct cgpu_info *cgpu)
 	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)
 {
 	struct timeval diff;
@@ -7138,6 +7158,8 @@ void print_summary(void)
 	for (i = 0; i < total_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);
 	}
 
@@ -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);
 
 		pool_resus(pool);
+		switch_pools(NULL);
 	} else
 		pool_died(pool);
 
@@ -7728,19 +7751,15 @@ static void probe_pools(void)
 #ifdef USE_USBUTILS
 static void *libusb_poll_thread(void __maybe_unused *arg)
 {
-	struct timeval tv = { 0, USB_ASYNC_POLL * 1000 };
-
 	RenameThread("usbpoll");
 
 	pthread_detach(pthread_self());
-	while (42)
-		libusb_handle_events_timeout(NULL, &tv);
+	while (usb_polling)
+		libusb_handle_events(NULL);
 
 	return NULL;
 }
 
-static pthread_t usb_poll_thread;
-
 static void initialise_usb(void) {
 	int err = libusb_init(NULL);
 	if (err) {
@@ -7751,6 +7770,7 @@ static void initialise_usb(void) {
 	mutex_init(&cgusb_lock);
 	mutex_init(&cgusbres_lock);
 	cglock_init(&cgusb_fd_lock);
+	usb_polling = true;
 	pthread_create(&usb_poll_thread, NULL, libusb_poll_thread, NULL);
 }
 #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 */
 	avalon->usbdev->usb_type = USB_TYPE_STD;
 	usb_set_pps(avalon, AVALON_USB_PACKETSIZE);
+	usb_buffer_enable(avalon);
 
 	/* We have a real 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 */
 #define BF1WAIT 1600
+#define BF1MSGSIZE 7
+#define BF1INFOSIZE 14
 
 static void bitfury_empty_buffer(struct cgpu_info *bitfury)
 {
@@ -26,18 +28,29 @@ static void bitfury_empty_buffer(struct cgpu_info *bitfury)
 	} while (amount);
 }
 
-static void bitfury_open(struct cgpu_info *bitfury)
+static int bitfury_open(struct cgpu_info *bitfury)
 {
 	uint32_t buf[2];
+	int err;
 
 	bitfury_empty_buffer(bitfury);
 	/* Magic sequence to reset device only really needed for windows but
 	 * harmless on linux. */
 	buf[0] = 0x80250000;
 	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)
@@ -63,15 +76,15 @@ static bool bitfury_getinfo(struct cgpu_info *bitfury, struct bitfury_info *info
 		       bitfury->drv->name, bitfury->device_id);
 		return false;
 	}
-	err = usb_read(bitfury, buf, 14, &amount, C_BF1_GETINFO);
+	err = usb_read(bitfury, buf, BF1INFOSIZE, &amount, C_BF1_GETINFO);
 	if (err) {
 		applog(LOG_INFO, "%s %d: Failed to read GETINFO",
 		       bitfury->drv->name, bitfury->device_id);
 		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;
 	}
 	info->version = buf[1];
@@ -95,15 +108,16 @@ static bool bitfury_reset(struct cgpu_info *bitfury)
 		       bitfury->drv->name, bitfury->device_id);
 		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) {
 		applog(LOG_INFO, "%s %d: Failed to read GETRESET",
 		       bitfury->drv->name, bitfury->device_id);
 		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;
 	}
 	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)
 		quit(1, "Failed to calloc info in bitfury_detect_one");
 	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);
 
-	bitfury_open(bitfury);
+	if (!bitfury_open(bitfury))
+		goto out_close;
 
 	/* Send getinfo request */
 	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);
 
 	if (!add_cgpu(bitfury))
-		goto out_close;
+		quit(1, "Failed to add_cgpu in bitfury_detect_one");
 
 	update_usb_stats(bitfury);
 	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 bitfury_info *info = bitfury->device_data;
 	struct timeval tv_now;
+	double nonce_rate;
+	int64_t ret = 0;
 	int amount, i;
 	char buf[45];
 	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);
 	if (unlikely(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;
 	while (amount) {
 		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);
 	cgtime(&info->tv_start);
 	/* 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 */
 	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
 	 * 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;
 		int j;
 
@@ -279,23 +300,30 @@ cascade:
 		info->prevwork[i] = info->prevwork[i - 1];
 	info->prevwork[0] = copy_work(work);
 	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)) {
 		applog(LOG_WARNING, "%s %d: Device disappeared, disabling thread",
 		       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)
 {
 	struct bitfury_info *info = cgpu->device_data;
 	struct api_data *root = NULL;
+	double nonce_rate;
 	char serial[16];
 	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);
 	sprintf(serial, "%08x", info->serial);
 	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;
 }

+ 3 - 0
driver-bitfury.h

@@ -24,6 +24,9 @@ struct bitfury_info {
 	char buf[512];
 	int tot;
 	int nonces;
+	int total_nonces;
+	double saved_nonces;
+	int cycles;
 	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 NANOSEC 1000000000.0
 
+#define CAIRNSMORE2_INTS 4
+
 // Icarus Rev3 doesn't send a completion message when it finishes
 // the full nonce range, so to avoid being idle we must abort the
 // 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";
 
 struct ICARUS_INFO {
+	enum sub_ident ident;
 	int intinfo;
 
 	// time to calculate the golden_ob
@@ -216,17 +219,43 @@ struct ICARUS_INFO {
 	int fpga_count;
 	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_UNUSED_SIZE 20
+#define ICARUS_UNUSED_SIZE 16
 #define ICARUS_WORK_SIZE 12
 
 #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 {
 	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 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",
 				icarus->drv->name, icarus->cgminer_id, ident);
 	}
-
-	info->initialised = true;
 }
 
 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;
 	int baud, uninitialised_var(work_division), uninitialised_var(fpga_count);
 	struct cgpu_info *icarus;
-	int ret, err, amount, tries;
-	enum sub_ident ident;
+	int ret, err, amount, tries, i;
 	bool ok;
+	bool cmr2_ok[CAIRNSMORE2_INTS];
+	int cmr2_count;
 
 	if ((sizeof(workdata) << 1) != (sizeof(golden_ob) - 1))
 		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");
 	icarus->device_data = (void *)info;
 
-	ident = usb_ident(icarus);
-	switch (ident) {
+	info->ident = usb_ident(icarus);
+	switch (info->ident) {
 		case IDENT_ICA:
 		case IDENT_BLT:
 		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;
 			break;
 		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;
+			cmr2_count = 0;
+			for (i = 0; i < CAIRNSMORE2_INTS; i++)
+				cmr2_ok[i] = false;
 			break;
 		default:
 			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;
 	ok = false;
 	while (!ok && tries-- > 0) {
 		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))
 			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)
 			ok = true;
 		else {
-			if (tries < 0) {
+			if (tries < 0 && info->ident != IDENT_CMR2) {
 				applog(LOG_ERR,
 					"Icarus Detect: "
 					"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);
 	}
 
-	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! */
 	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",
 		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",
 		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);
 	
-	if (usb_ident(icarus) == IDENT_CMR2) {
+	if (info->ident == IDENT_CMR2) {
 		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 ICARUS_INFO *intmp;
 
+			if (!cmr2_ok[i])
+				continue;
+
 			cgtmp = usb_copy_cgpu(icarus);
 			if (!cgtmp) {
 				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;
 }
 
+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,
 				__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)
 		return -1;
 
-	if (!info->initialised)
-		icarus_initialise(icarus, info->baud);
-
 	elapsed.tv_sec = elapsed.tv_usec = 0;
 
 	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.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
 	usb_buffer_clear(icarus);
 
@@ -1245,17 +1377,80 @@ static struct api_data *icarus_api_stats(struct cgpu_info *cgpu)
 	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)
 {
 	// 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 = {
 	.drv_id = DRIVER_icarus,
 	.dname = "Icarus",
 	.name = "ICA",
 	.drv_detect = icarus_detect,
 	.get_api_stats = icarus_api_stats,
+	.get_statline_before = icarus_statline_before,
+	.set_device = icarus_set,
+	.identify_device = icarus_identify,
 	.thread_prepare = icarus_prepare,
 	.scanhash = icarus_scanhash,
 	.thread_shutdown = icarus_shutdown,

+ 8 - 1
miner.h

@@ -472,7 +472,6 @@ struct cgpu_info {
 #endif
 #ifdef USE_USBUTILS
 	struct cg_usb_info usbinfo;
-	int usb_cancels;
 #endif
 #ifdef USE_MODMINER
 	char fpgaid;
@@ -881,6 +880,14 @@ static inline void cg_runlock(cglock_t *lock)
 	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)
 {
 	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 cgpu_info *lookcgpu;
-	int i, pstate;
+	int i;
 
-	DEVWLOCK(cgpu, pstate);
 	// It has already been done
 	if (cgpu->usbinfo.nodev)
-		goto out_unlock;
+		return;
 
 	applog(LOG_DEBUG, "USB release %s%i",
 			cgpu->drv->name, cgpu->device_id);
@@ -1335,8 +1334,6 @@ static void release_cgpu(struct cgpu_info *cgpu)
 
 	_usb_uninit(cgpu);
 	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:
 			item = CMD_CMD;
 			break;
-		case LIBUSB_TRANSFER_TIMED_OUT:
 		case LIBUSB_ERROR_TIMEOUT:
 			item = CMD_TIMEOUT;
 			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
  * 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 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;
 
 	cgtime(&tv_now);
-	timeout = timeout + USB_ASYNC_POLL;
 	ms_to_timespec(&ts_end, timeout);
 	timeval_to_spec(&ts_now, &tv_now);
 	timeraddspec(&ts_end, &ts_now);
 	ret = pthread_cond_timedwait(&ut->cond, &ut->mutex, &ts_end);
 	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);
-		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. */
 		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;
-	} else
-		ret = transfer->status;
+
 	/* No need to sort out mutexes here since they won't be reused */
 	*transferred = transfer->actual_length;
 	libusb_free_transfer(transfer);
@@ -2295,8 +2283,9 @@ usb_bulk_transfer(struct libusb_device_handle *dev_handle, int intinfo,
 
 	init_usb_transfer(&ut);
 	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,
-				  bulk_callback, &ut, timeout);
+				  bulk_callback, &ut, 0);
 
 	STATS_TIMEVAL(&tv_start);
 	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);
 	errn = errno;
 	if (!err)
-		err = callback_wait(cgpu, &ut, transferred, timeout);
+		err = callback_wait(&ut, transferred, timeout);
 
 	STATS_TIMEVAL(&tv_finish);
 	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);
 
 	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)
 		memcpy(data, buf, length);
@@ -2599,10 +2592,12 @@ out_unlock:
 			err = LIBUSB_ERROR_OTHER;
 	}
 out_noerrmsg:
-	DEVRUNLOCK(cgpu, pstate);
-
-	if (NODEV(err))
+	if (NODEV(err)) {
+		cg_ruwlock(&cgpu->usbinfo.devlock);
 		release_cgpu(cgpu);
+		DEVWUNLOCK(cgpu, pstate);
+	} else
+		DEVRUNLOCK(cgpu, pstate);
 
 	return err;
 }
@@ -2697,10 +2692,12 @@ int _usb_write(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_
 			err = LIBUSB_ERROR_OTHER;
 	}
 out_noerrmsg:
-	DEVRUNLOCK(cgpu, pstate);
-
-	if (NODEV(err))
+	if (NODEV(err)) {
+		cg_ruwlock(&cgpu->usbinfo.devlock);
 		release_cgpu(cgpu);
+		DEVWUNLOCK(cgpu, pstate);
+	} else
+		DEVRUNLOCK(cgpu, pstate);
 
 	return err;
 }
@@ -2772,8 +2769,8 @@ int __usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bReques
 	IOERR_CHECK(cgpu, err);
 
 	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_:
 	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);
 
-	DEVRUNLOCK(cgpu, pstate);
-
-	if (NOCONTROLDEV(err))
+	if (NOCONTROLDEV(err)) {
+		cg_ruwlock(&cgpu->usbinfo.devlock);
 		release_cgpu(cgpu);
+		cg_dwlock(&cgpu->usbinfo.devlock);
+	}
+
+	DEVRUNLOCK(cgpu, pstate);
 
 	return err;
 }
@@ -2856,14 +2856,17 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe
 		err = 0;
 	}
 	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:
-	DEVRUNLOCK(cgpu, pstate);
-
-	if (NOCONTROLDEV(err))
+	if (NOCONTROLDEV(err)) {
+		cg_ruwlock(&cgpu->usbinfo.devlock);
 		release_cgpu(cgpu);
+		cg_dwlock(&cgpu->usbinfo.devlock);
+	}
+
+	DEVRUNLOCK(cgpu, pstate);
 
 	return err;
 }
@@ -3129,7 +3132,7 @@ void usb_set_dev_start(struct cgpu_info *cgpu)
 void usb_cleanup(void)
 {
 	struct cgpu_info *cgpu;
-	int count;
+	int count, pstate;
 	int i;
 
 	hotplug_time = 0;
@@ -3147,7 +3150,9 @@ void usb_cleanup(void)
 			case DRIVER_icarus:
 			case DRIVER_avalon:
 			case DRIVER_klondike:
+				DEVWLOCK(cgpu, pstate);
 				release_cgpu(cgpu);
+				DEVWUNLOCK(cgpu, pstate);
 				count++;
 				break;
 			default:
@@ -3186,7 +3191,7 @@ void usb_cleanup(void)
 	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; \
 	found = true; \
 	}

+ 1 - 1
util.c

@@ -2006,7 +2006,7 @@ static bool setup_stratum_socket(struct pool *pool)
 		break;
 	}
 	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);
 		freeaddrinfo(servinfo);
 		return false;