Browse Source

Merge branch 'bfl'

Conflicts:
	miner.h
Con Kolivas 13 years ago
parent
commit
732783faa6
3 changed files with 347 additions and 132 deletions
  1. 57 39
      cgminer.c
  2. 283 93
      driver-bitforce.c
  3. 7 0
      miner.h

+ 57 - 39
cgminer.c

@@ -3373,10 +3373,6 @@ static void hashmeter(int thr_id, struct timeval *diff,
 		thr_info[thr_id].cgpu->device_last_well = time(NULL);
 	}
 
-	/* Don't bother calculating anything if we're not displaying it */
-	if (opt_realquiet || !opt_log_interval)
-		return;
-
 	secs = (double)diff->tv_sec + ((double)diff->tv_usec / 1000000.0);
 
 	/* So we can call hashmeter from a non worker thread */
@@ -4170,6 +4166,7 @@ disabled:
 				tq_pop(mythr->q, NULL); /* Ignore ping that's popped */
 				thread_reportin(mythr);
 				applog(LOG_WARNING, "Thread %d being re-enabled", thr_id);
+				if (api->thread_enable) api->thread_enable(mythr);
 			}
 
 			sdiff.tv_sec = sdiff.tv_usec = 0;
@@ -4455,9 +4452,16 @@ static void age_work(void)
 /* Makes sure the hashmeter keeps going even if mining threads stall, updates
  * the screen at regular intervals, and restarts threads if they appear to have
  * died. */
+#define WATCHDOG_INTERVAL		3
+#define WATCHDOG_SICK_TIME		60
+#define WATCHDOG_DEAD_TIME		600
+#define WATCHDOG_SICK_COUNT		(WATCHDOG_SICK_TIME/WATCHDOG_INTERVAL)
+#define WATCHDOG_DEAD_COUNT		(WATCHDOG_DEAD_TIME/WATCHDOG_INTERVAL)
+#define WATCHDOG_LOW_HASH		1.0 /* consider < 1MH too low for any device */
+
 static void *watchdog_thread(void __maybe_unused *userdata)
 {
-	const unsigned int interval = 3;
+	const unsigned int interval = WATCHDOG_INTERVAL;
 	struct timeval zero_tv;
 
 	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
@@ -4530,24 +4534,27 @@ static void *watchdog_thread(void __maybe_unused *userdata)
 			}
 		}
 
-#ifdef HAVE_OPENCL
 		for (i = 0; i < total_devices; ++i) {
 			struct cgpu_info *cgpu = devices[i];
 			struct thr_info *thr = cgpu->thr[0];
 			enum dev_enable *denable;
+			bool dev_count_well;
+			bool dev_count_sick;
+			bool dev_count_dead;
+			char dev_str[8];
 			int gpu;
 
-			if (cgpu->api != &opencl_api)
-				continue;
-			/* Use only one thread per device to determine if the GPU is healthy */
-			if (i >= nDevs)
-				break;
-			gpu = thr->cgpu->device_id;
+			if (cgpu->api->get_stats)
+			  cgpu->api->get_stats(cgpu);
+
+			gpu = cgpu->device_id;
 			denable = &cgpu->deven;
+			sprintf(dev_str, "%s%d", cgpu->api->name, gpu);
+
 #ifdef HAVE_ADL
-			if (adl_active && gpus[gpu].has_adl)
+			if (adl_active && cgpu->has_adl)
 				gpu_autotune(gpu, denable);
-			if (opt_debug && gpus[gpu].has_adl) {
+			if (opt_debug && cgpu->has_adl) {
 				int engineclock = 0, memclock = 0, activity = 0, fanspeed = 0, fanpercent = 0, powertune = 0;
 				float temp = 0, vddc = 0;
 
@@ -4556,55 +4563,64 @@ static void *watchdog_thread(void __maybe_unused *userdata)
 					temp, fanpercent, fanspeed, engineclock, memclock, vddc, activity, powertune);
 			}
 #endif
+			
 			/* Thread is waiting on getwork or disabled */
 			if (thr->getwork || *denable == DEV_DISABLED)
 				continue;
 
-			if (gpus[gpu].status != LIFE_WELL && now.tv_sec - thr->last.tv_sec < 60) {
-				applog(LOG_ERR, "Device %d recovered, GPU %d declared WELL!", i, gpu);
-				gpus[gpu].status = LIFE_WELL;
-				gpus[gpu].device_last_well = time(NULL);
-			} else if (now.tv_sec - thr->last.tv_sec > 60 && gpus[gpu].status == LIFE_WELL) {
-				thr->rolling = thr->cgpu->rolling = 0;
-				gpus[gpu].status = LIFE_SICK;
-				applog(LOG_ERR, "Device %d idle for more than 60 seconds, GPU %d declared SICK!", i, gpu);
+			if (cgpu->rolling < WATCHDOG_LOW_HASH)
+				cgpu->low_count++;
+			else
+				cgpu->low_count = 0;
+
+			dev_count_well = (cgpu->low_count < WATCHDOG_SICK_COUNT);
+			dev_count_sick = (cgpu->low_count > WATCHDOG_SICK_COUNT);
+			dev_count_dead = (cgpu->low_count > WATCHDOG_DEAD_COUNT);
+
+			if (gpus[gpu].status != LIFE_WELL && (now.tv_sec - thr->last.tv_sec < WATCHDOG_SICK_TIME) && dev_count_well) {
+				applog(LOG_ERR, "%s: Recovered, declaring WELL!", dev_str);
+				cgpu->status = LIFE_WELL;
+				cgpu->device_last_well = time(NULL);
+			} else if (cgpu->status == LIFE_WELL && ((now.tv_sec - thr->last.tv_sec > WATCHDOG_SICK_TIME) || dev_count_sick)) {
+				thr->rolling = cgpu->rolling = 0;
+				cgpu->status = LIFE_SICK;
+				applog(LOG_ERR, "%s: Idle for more than 60 seconds, declaring SICK!", dev_str);
 				gettimeofday(&thr->sick, NULL);
 
-				gpus[gpu].device_last_not_well = time(NULL);
-				gpus[gpu].device_not_well_reason = REASON_DEV_SICK_IDLE_60;
-				gpus[gpu].dev_sick_idle_60_count++;
+				cgpu->device_last_not_well = time(NULL);
+				cgpu->device_not_well_reason = REASON_DEV_SICK_IDLE_60;
+				cgpu->dev_sick_idle_60_count++;
 #ifdef HAVE_ADL
-				if (adl_active && gpus[gpu].has_adl && gpu_activity(gpu) > 50) {
+				if (adl_active && cgpu->has_adl && gpu_activity(gpu) > 50) {
 					applog(LOG_ERR, "GPU still showing activity suggesting a hard hang.");
 					applog(LOG_ERR, "Will not attempt to auto-restart it.");
 				} else
 #endif
 				if (opt_restart) {
-					applog(LOG_ERR, "Attempting to restart GPU");
-					reinit_device(thr->cgpu);
+					applog(LOG_ERR, "%s: Attempting to restart", dev_str);
+					reinit_device(cgpu);
 				}
-			} else if (now.tv_sec - thr->last.tv_sec > 600 && gpus[i].status == LIFE_SICK) {
-				gpus[gpu].status = LIFE_DEAD;
-				applog(LOG_ERR, "Device %d not responding for more than 10 minutes, GPU %d declared DEAD!", i, gpu);
+			} else if (cgpu->status == LIFE_SICK && ((now.tv_sec - thr->last.tv_sec > WATCHDOG_DEAD_TIME) || dev_count_dead)) {
+				cgpu->status = LIFE_DEAD;
+				applog(LOG_ERR, "%s: Not responded for more than 10 minutes, declaring DEAD!", dev_str);
 				gettimeofday(&thr->sick, NULL);
 
-				gpus[gpu].device_last_not_well = time(NULL);
-				gpus[gpu].device_not_well_reason = REASON_DEV_DEAD_IDLE_600;
-				gpus[gpu].dev_dead_idle_600_count++;
+				cgpu->device_last_not_well = time(NULL);
+				cgpu->device_not_well_reason = REASON_DEV_DEAD_IDLE_600;
+				cgpu->dev_dead_idle_600_count++;
 			} else if (now.tv_sec - thr->sick.tv_sec > 60 &&
-				   (gpus[i].status == LIFE_SICK || gpus[i].status == LIFE_DEAD)) {
+				   (cgpu->status == LIFE_SICK || cgpu->status == LIFE_DEAD)) {
 				/* Attempt to restart a GPU that's sick or dead once every minute */
 				gettimeofday(&thr->sick, NULL);
 #ifdef HAVE_ADL
-				if (adl_active && gpus[gpu].has_adl && gpu_activity(gpu) > 50) {
+				if (adl_active && cgpu->has_adl && gpu_activity(gpu) > 50) {
 					/* Again do not attempt to restart a device that may have hard hung */
 				} else
 #endif
 				if (opt_restart)
-					reinit_device(thr->cgpu);
+					reinit_device(cgpu);
 			}
 		}
-#endif
 	}
 
 	return NULL;
@@ -5482,7 +5498,7 @@ begin_bench:
 		quit(1, "tq_new failed for gpur_thr_id");
 	if (thr_info_create(thr, NULL, reinit_gpu, thr))
 		quit(1, "reinit_gpu thread create failed");
-#endif
+#endif	
 
 	/* Create API socket thread */
 	api_thr_id = mining_threads + 5;
@@ -5491,6 +5507,7 @@ begin_bench:
 		quit(1, "API thread create failed");
 	pthread_detach(thr->pth);
 
+
 #ifdef HAVE_CURSES
 	/* Create curses input thread for keyboard input. Create this last so
 	 * that we know all threads are created since this can call kill_work
@@ -5529,3 +5546,4 @@ begin_bench:
 
 	return 0;
 }
+

+ 283 - 93
driver-bitforce.c

@@ -20,6 +20,11 @@
 #include "fpgautils.h"
 #include "miner.h"
 
+#define BITFORCE_SLEEP_MS 3000
+#define BITFORCE_TIMEOUT_MS 10000
+#define BITFORCE_CHECK_INTERVAL_MS 10
+#define WORK_CHECK_INTERVAL_MS 50
+#define MAX_START_DELAY_US 100000
 
 struct device_api bitforce_api;
 
@@ -29,71 +34,71 @@ static void BFgets(char *buf, size_t bufLen, int fd)
 {
 	do
 		--bufLen;
-	while (likely(bufLen && read(fd, buf, 1) && (buf++)[0] != '\n'))
-		;
+	while (likely(bufLen && read(fd, buf, 1) && (buf++)[0] != '\n'));
+
 	buf[0] = '\0';
 }
 
-static ssize_t BFwrite2(int fd, const void *buf, ssize_t bufLen)
+static ssize_t BFwrite(int fd, const void *buf, ssize_t bufLen)
 {
-	return write(fd, buf, bufLen);
+	if ((bufLen) != write(fd, buf, bufLen))
+		return 0;
+	else
+		return bufLen;
 }
 
-#define BFwrite(fd, buf, bufLen)  do {  \
-	if ((bufLen) != BFwrite2(fd, buf, bufLen)) {  \
-		applog(LOG_ERR, "Error writing to BitForce (" #buf ")");  \
-		return 0;  \
-	}  \
-} while(0)
-
 #define BFclose(fd) close(fd)
 
 static bool bitforce_detect_one(const char *devpath)
 {
-	char *s;
+	int fdDev = BFopen(devpath);
+	struct cgpu_info *bitforce;
 	char pdevbuf[0x100];
+	char *s;
 
-	applog(LOG_DEBUG, "BitForce Detect: Attempting to open %s", devpath);
+	applog(LOG_DEBUG, "BFL: Attempting to open %s", devpath);
 
-	int fdDev = BFopen(devpath);
 	if (unlikely(fdDev == -1)) {
-		applog(LOG_ERR, "BitForce Detect: Failed to open %s", devpath);
+		applog(LOG_ERR, "BFL: Failed to open %s", devpath);
 		return false;
 	}
+
 	BFwrite(fdDev, "ZGX", 3);
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "Error reading from BitForce (ZGX)");
+		applog(LOG_ERR, "BFL: Error reading (ZGX)");
 		return 0;
 	}
+
 	BFclose(fdDev);
 	if (unlikely(!strstr(pdevbuf, "SHA256"))) {
-		applog(LOG_DEBUG, "BitForce Detect: Didn't recognise BitForce on %s", devpath);
+		applog(LOG_ERR, "BFL: Didn't recognise BitForce on %s", devpath);
 		return false;
 	}
 
 	// We have a real BitForce!
-	struct cgpu_info *bitforce;
 	bitforce = calloc(1, sizeof(*bitforce));
 	bitforce->api = &bitforce_api;
 	bitforce->device_path = strdup(devpath);
 	bitforce->deven = DEV_ENABLED;
 	bitforce->threads = 1;
-	if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>"))))
-	{
+	bitforce->sleep_ms = BITFORCE_SLEEP_MS;
+
+	if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>")))) {
 		s[0] = '\0';
 		bitforce->name = strdup(pdevbuf + 7);
 	}
+	
+	mutex_init(&bitforce->device_mutex);
 
 	return add_cgpu(bitforce);
 }
 
 static char bitforce_detect_auto()
 {
-	return
-	serial_autodetect_udev     (bitforce_detect_one, "BitFORCE*SHA256") ?:
-	serial_autodetect_devserial(bitforce_detect_one, "BitFORCE_SHA256") ?:
-	0;
+	return (serial_autodetect_udev     (bitforce_detect_one, "BitFORCE*SHA256") ?:
+		serial_autodetect_devserial(bitforce_detect_one, "BitFORCE_SHA256") ?:
+		0);
 }
 
 static void bitforce_detect()
@@ -104,6 +109,7 @@ static void bitforce_detect()
 static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
 {
 	float gt = bitforce->temp;
+
 	if (gt > 0)
 		tailsprintf(buf, "%5.1fC ", gt);
 	else
@@ -114,79 +120,112 @@ static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
 static bool bitforce_thread_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
-
+	int fdDev = BFopen(bitforce->device_path);
 	struct timeval now;
 
-	int fdDev = BFopen(bitforce->device_path);
-	if (unlikely(-1 == fdDev)) {
-		applog(LOG_ERR, "Failed to open BitForce on %s", bitforce->device_path);
+	if (unlikely(fdDev == -1)) {
+		applog(LOG_ERR, "BFL%i: Failed to open %s", bitforce->device_id, bitforce->device_path);
 		return false;
 	}
 
 	bitforce->device_fd = fdDev;
 
-	applog(LOG_INFO, "Opened BitForce on %s", bitforce->device_path);
+	applog(LOG_INFO, "BFL%i: Opened %s", bitforce->device_id, bitforce->device_path);
 	gettimeofday(&now, NULL);
 	get_datestamp(bitforce->init, &now);
 
 	return true;
 }
 
-static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint64_t __maybe_unused max_nonce)
+static void biforce_clear_buffer(struct cgpu_info *bitforce)
 {
-	struct cgpu_info *bitforce = thr->cgpu;
 	int fdDev = bitforce->device_fd;
+	char pdevbuf[0x100];
 
-	int i, thr_id = thr->id;
+	applog(LOG_DEBUG, "BFL%i: Clearing read buffer", bitforce->device_id);
+
+	mutex_lock(&bitforce->device_mutex);
+	do {
+		pdevbuf[0] = '\0';
+		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
+	} while (pdevbuf[0]);
+	mutex_unlock(&bitforce->device_mutex);
+}
+
+void bitforce_init(struct cgpu_info *bitforce)
+{
+	char *devpath = bitforce->device_path;
+	int fdDev = bitforce->device_fd;
 	char pdevbuf[0x100];
-	unsigned char ob[61] = ">>>>>>>>12345678901234567890123456789012123456789012>>>>>>>>";
-	struct timeval tdiff;
-	char *pnoncebuf;
 	char *s;
-	uint32_t nonce;
 
-	BFwrite(fdDev, "ZDX", 3);
-	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "Error reading from BitForce (ZDX)");
-		return 0;
-	}
-	if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) {
-		applog(LOG_ERR, "BitForce ZDX reports: %s", pdevbuf);
-		return 0;
-	}
+	applog(LOG_WARNING, "BFL%i: Re-initalizing", bitforce->device_id);
 
-	memcpy(ob + 8, work->midstate, 32);
-	memcpy(ob + 8 + 32, work->data + 64, 12);
-	BFwrite(fdDev, ob, 60);
-	if (opt_debug) {
-		s = bin2hex(ob + 8, 44);
-		applog(LOG_DEBUG, "BitForce block data: %s", s);
-		free(s);
+	biforce_clear_buffer(bitforce);
+
+	mutex_lock(&bitforce->device_mutex);
+	if (fdDev)
+		BFclose(fdDev);
+	bitforce->device_fd = 0;
+
+	fdDev = BFopen(devpath);
+	if (unlikely(fdDev == -1)) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "BFL%i: Failed to open %s", bitforce->device_id, devpath);
+		return;
 	}
 
+	BFwrite(fdDev, "ZGX", 3);
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
+	
 	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "Error reading from BitForce (block data)");
-		return 0;
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "BFL%i: Error reading (ZGX)", bitforce->device_id);
+		return;
 	}
-	if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) {
-		applog(LOG_ERR, "BitForce block data reports: %s", pdevbuf);
-		return 0;
+
+	if (unlikely(!strstr(pdevbuf, "SHA256"))) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "BFL%i: Didn't recognise BitForce on %s returned: %s", bitforce->device_id, devpath, pdevbuf);
+		return;
 	}
+	
+	if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>")))) {
+		s[0] = '\0';
+		bitforce->name = strdup(pdevbuf + 7);
+	}
+
+	bitforce->device_fd = fdDev;
+	mutex_unlock(&bitforce->device_mutex);
+}
 
+static bool bitforce_get_temp(struct cgpu_info *bitforce)
+{
+	int fdDev = bitforce->device_fd;
+	char pdevbuf[0x100];
+	char *s;
+
+	if (!fdDev)
+		return false;
+
+	mutex_lock(&bitforce->device_mutex);
 	BFwrite(fdDev, "ZLX", 3);
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
+	mutex_unlock(&bitforce->device_mutex);
+	
 	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "Error reading from BitForce (ZKX)");
-		return 0;
+		applog(LOG_ERR, "BFL%i: Error: Get temp returned empty string", bitforce->device_id);
+		bitforce->temp = 0;
+		return false;
 	}
+
 	if ((!strncasecmp(pdevbuf, "TEMP", 4)) && (s = strchr(pdevbuf + 4, ':'))) {
 		float temp = strtof(s + 1, NULL);
+
 		if (temp > 0) {
 			bitforce->temp = temp;
 			if (temp > bitforce->cutofftemp) {
-				applog(LOG_WARNING, "Hit thermal cutoff limit on %s %d, disabling!", bitforce->api->name, bitforce->device_id);
+				applog(LOG_WARNING, "BFL%i: Hit thermal cutoff limit, disabling!", bitforce->device_id);
 				bitforce->deven = DEV_RECOVER;
 
 				bitforce->device_last_not_well = time(NULL);
@@ -195,46 +234,114 @@ static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint6
 			}
 		}
 	}
+	return true;
+}
 
-	/* Initially wait 2/3 of the average cycle time so we can request more
-	 * work before full scan is up ~ 3.4 seconds */
-	tdiff.tv_sec = 3;
-	tdiff.tv_usec = 4000000;
-	if (!restart_wait(&tdiff))
-		return 0;
-	queue_request(thr, false);
-	i = 3400;
+static bool bitforce_send_work(struct thr_info *thr, struct work *work)
+{
+	unsigned char ob[61] = ">>>>>>>>12345678901234567890123456789012123456789012>>>>>>>>";
+	struct cgpu_info *bitforce = thr->cgpu;
+	int fdDev = bitforce->device_fd;
+	char pdevbuf[0x100];
+	char *s;
 
-	/* Now wait another second; no bistream should be finished by now */
-	tdiff.tv_sec = 1;
-	tdiff.tv_usec = 0;
-	if (!restart_wait(&tdiff))
-		return 0;
-	i += 1000;
+	if (!fdDev)
+		return false;
+re_send:
+	mutex_lock(&bitforce->device_mutex);
+	BFwrite(fdDev, "ZDX", 3);
+	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
+	if (!pdevbuf[0] || (pdevbuf[0] == 'B')) {
+		mutex_unlock(&bitforce->device_mutex);
+		bitforce->wait_ms += WORK_CHECK_INTERVAL_MS;
+		usleep(WORK_CHECK_INTERVAL_MS * 1000);
+		goto re_send;
+	} else if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "BFL%i: Error: Send work reports: %s", bitforce->device_id, pdevbuf);
+		return false;
+	}
+
+	memcpy(ob + 8, work->midstate, 32);
+	memcpy(ob + 8 + 32, work->data + 64, 12);
+
+	BFwrite(fdDev, ob, 60);
+	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
+	mutex_unlock(&bitforce->device_mutex);
+
+	if (opt_debug) {
+		s = bin2hex(ob + 8, 44);
+		applog(LOG_DEBUG, "BFL%i: block data: %s", bitforce->device_id, s);
+		free(s);
+	}
+
+	if (unlikely(!pdevbuf[0])) {
+		applog(LOG_ERR, "BFL%i: Error: Send block data returned empty string", bitforce->device_id);
+		return false;
+	}
 
-	/* Now start looking for results. Stupid polling every 10ms... */
-	while (42) {
-		if (unlikely(work_restart[thr_id].restart))
-			return 0;
-		usleep(10000);
-		i += 10;
+	if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) {
+		applog(LOG_ERR, "BFL%i: Error: Send block data reports: %s", bitforce->device_id, pdevbuf);
+		return false;
+	}
 
+	return true;
+}
+
+static uint64_t bitforce_get_result(struct thr_info *thr, struct work *work)
+{
+	unsigned int delay_time_ms = BITFORCE_CHECK_INTERVAL_MS;
+	struct cgpu_info *bitforce = thr->cgpu;
+	int fdDev = bitforce->device_fd;
+	char pdevbuf[0x100];
+	char *pnoncebuf;
+	uint32_t nonce;
+
+
+	if (!fdDev)
+		return 0;
+
+	while (bitforce->wait_ms < BITFORCE_TIMEOUT_MS) {
+		if (unlikely(work_restart[thr->id].restart))
+			return 1;
+		mutex_lock(&bitforce->device_mutex);
 		BFwrite(fdDev, "ZFX", 3);
 		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-		if (unlikely(!pdevbuf[0])) {
-			applog(LOG_ERR, "Error reading from BitForce (ZFX)");
-			return 0;
-		}
-		if (pdevbuf[0] != 'B')
-		    break;
+		mutex_unlock(&bitforce->device_mutex);
+		if (pdevbuf[0] && pdevbuf[0] != 'B') /* BFL does not respond during throttling */
+			break;
+		/* if BFL is throttling, no point checking so quickly */
+		delay_time_ms = (pdevbuf[0] ? BITFORCE_CHECK_INTERVAL_MS : 2*WORK_CHECK_INTERVAL_MS);
+		usleep(delay_time_ms * 1000);
+		bitforce->wait_ms += delay_time_ms;
+	}
+
+	if (bitforce->wait_ms >= BITFORCE_TIMEOUT_MS) {
+		applog(LOG_ERR, "BFL%i: took longer than 10s", bitforce->device_id);
+		bitforce->device_last_not_well = time(NULL);
+		bitforce->device_not_well_reason = REASON_DEV_OVER_HEAT;
+		bitforce->dev_over_heat_count++;
+		return 1;
+	} else if (pdevbuf[0] == 'N') {/* Hashing complete (NONCE-FOUND or NO-NONCE) */
+		    /* Simple timing adjustment */
+	        delay_time_ms = bitforce->sleep_ms;
+		if (bitforce->wait_ms > (bitforce->sleep_ms + BITFORCE_CHECK_INTERVAL_MS))
+			bitforce->sleep_ms += (unsigned int) ((double) (bitforce->wait_ms - bitforce->sleep_ms) / 1.6);
+		else if (bitforce->wait_ms == bitforce->sleep_ms)
+			bitforce->sleep_ms -= BITFORCE_CHECK_INTERVAL_MS;
+		if (delay_time_ms != bitforce->sleep_ms)
+			  applog(LOG_DEBUG, "BFL%i: Wait time changed to: %d. Waited: %d", bitforce->device_id, bitforce->sleep_ms, bitforce->wait_ms);
 	}
-	applog(LOG_DEBUG, "BitForce waited %dms until %s\n", i, pdevbuf);
+
+	applog(LOG_DEBUG, "BFL%i: waited %dms until %s", bitforce->device_id, bitforce->wait_ms, pdevbuf);
 	work->blk.nonce = 0xffffffff;
-	if (pdevbuf[2] == '-')
-		return 0xffffffff;
+	if (pdevbuf[2] == '-') 
+		return 0xffffffff;   /* No valid nonce found */
+	else if (pdevbuf[0] == 'I') 
+		return 1;          /* Device idle */
 	else if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) {
-		applog(LOG_ERR, "BitForce result reports: %s", pdevbuf);
-		return 0;
+		applog(LOG_WARNING, "BFL%i: Error: Get result reports: %s", bitforce->device_id, pdevbuf);
+		return 1;
 	}
 
 	pnoncebuf = &pdevbuf[12];
@@ -244,7 +351,6 @@ static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint6
 #ifndef __BIG_ENDIAN__
 		nonce = swab32(nonce);
 #endif
-
 		submit_nonce(thr, work, nonce);
 		if (pnoncebuf[8] != ',')
 			break;
@@ -254,11 +360,95 @@ static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint6
 	return 0xffffffff;
 }
 
+static void bitforce_shutdown(struct thr_info *thr)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+
+	BFclose(bitforce->device_fd);
+	bitforce->device_fd = 0;
+}
+
+static void biforce_thread_enable(struct thr_info *thr)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+
+	bitforce_init(bitforce);
+}
+
+static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint64_t __maybe_unused max_nonce)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	unsigned int sleep_time;
+	struct timeval tdiff;
+	uint64_t ret;
+
+	bitforce->wait_ms = 0;
+	ret = bitforce_send_work(thr, work);
+
+	/* Initially wait 2/3 of the average cycle time so we can request more
+	work before full scan is up */
+	sleep_time = (2 * bitforce->sleep_ms) / 3;
+	tdiff.tv_sec = sleep_time / 1000;
+	tdiff.tv_usec = sleep_time * 1000 - (tdiff.tv_sec * 1000000);
+	if (!restart_wait(&tdiff))
+		return 1;
+
+	bitforce->wait_ms += sleep_time;
+	queue_request(thr, false);
+
+	/* Now wait athe final 1/3rd; no bitforce should be finished by now */
+	sleep_time = bitforce->sleep_ms - sleep_time;
+	tdiff.tv_sec = sleep_time / 1000;
+	tdiff.tv_usec = sleep_time * 1000 - (tdiff.tv_sec * 1000000);
+	if (!restart_wait(&tdiff))
+		return 1;
+
+	bitforce->wait_ms += sleep_time;
+
+	if (ret)
+		ret = bitforce_get_result(thr, work);
+
+	if (!ret) {
+		ret = 1;
+		applog(LOG_ERR, "BFL%i: Comms error", bitforce->device_id);
+		bitforce->device_last_not_well = time(NULL);
+		bitforce->device_not_well_reason = REASON_DEV_NOSTART;
+		bitforce->dev_nostart_count++;
+		/* empty read buffer */
+		biforce_clear_buffer(bitforce);
+	}
+	return ret;
+}
+
+static bool bitforce_get_stats(struct cgpu_info *bitforce)
+{
+	return bitforce_get_temp(bitforce);
+}
+
+static bool bitforce_thread_init(struct thr_info *thr)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	unsigned int wait;
+
+	/* Pause each new thread a random time between 0-100ms 
+	so the devices aren't making calls all at the same time. */
+	wait = (rand() * MAX_START_DELAY_US)/RAND_MAX;
+	applog(LOG_DEBUG, "BFL%i: Delaying start by %dms", bitforce->device_id, wait / 1000);
+	usleep(wait);
+
+	return true;
+}
+
 struct device_api bitforce_api = {
 	.dname = "bitforce",
 	.name = "BFL",
 	.api_detect = bitforce_detect,
+	.reinit_device = bitforce_init,
 	.get_statline_before = get_bitforce_statline_before,
+	.get_stats = bitforce_get_stats,
 	.thread_prepare = bitforce_thread_prepare,
+	.thread_init = bitforce_thread_init,
 	.scanhash = bitforce_scanhash,
+	.thread_shutdown = bitforce_shutdown,
+	.thread_enable = biforce_thread_enable
 };

+ 7 - 0
miner.h

@@ -236,6 +236,7 @@ struct device_api {
 	void (*get_statline_before)(char*, struct cgpu_info*);
 	void (*get_statline)(char*, struct cgpu_info*);
 	struct api_data *(*get_api_stats)(struct cgpu_info*);
+	bool (*get_stats)(struct cgpu_info*);
 
 	// Thread-specific functions
 	bool (*thread_prepare)(struct thr_info*);
@@ -245,6 +246,7 @@ struct device_api {
 	bool (*prepare_work)(struct thr_info*, struct work*);
 	uint64_t (*scanhash)(struct thr_info*, struct work*, uint64_t);
 	void (*thread_shutdown)(struct thr_info*);
+	void (*thread_enable)(struct thr_info*);
 };
 
 enum dev_enable {
@@ -315,12 +317,17 @@ struct cgpu_info {
 #endif
 		int device_fd;
 	};
+#ifdef USE_BITFORCE
+	unsigned int wait_ms;
+	unsigned int sleep_ms;
+#endif
 	pthread_mutex_t		device_mutex;
 
 	enum dev_enable deven;
 	int accepted;
 	int rejected;
 	int hw_errors;
+	unsigned int low_count;
 	double rolling;
 	double total_mhashes;
 	double utility;