Browse Source

Merge branch 'usb-dev' of github.com:ckolivas/cgminer into usb-dev

ckolivas 13 years ago
parent
commit
22a50a5a0f
26 changed files with 2124 additions and 1019 deletions
  1. 60 2
      API-README
  2. BIN
      API.class
  3. 11 2
      API.java
  4. 14 11
      FPGA-README
  5. 5 1
      Makefile.am
  6. 11 9
      README
  7. 1 1
      adl.c
  8. 153 62
      api.c
  9. 443 141
      cgminer.c
  10. 4 3
      configure.ac
  11. 359 321
      driver-bitforce.c
  12. 5 4
      driver-cpu.c
  13. 1 1
      driver-cpu.h
  14. 10 9
      driver-icarus.c
  15. 138 122
      driver-modminer.c
  16. 24 34
      driver-opencl.c
  17. 1 1
      driver-opencl.h
  18. 9 6
      driver-ztex.c
  19. 1 1
      findnonce.c
  20. 5 5
      fpgautils.c
  21. 7 7
      fpgautils.h
  22. 42 8
      miner.h
  23. 239 50
      miner.php
  24. 486 203
      usbutils.c
  25. 65 11
      usbutils.h
  26. 30 4
      util.c

+ 60 - 2
API-README

@@ -347,6 +347,18 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               The current options are:
                               The current options are:
                                MMQ opt=clock val=160 to 230 (and a multiple of 2)
                                MMQ opt=clock val=160 to 230 (and a multiple of 2)
 
 
+ zero|Which,true/false (*)
+               none           There is no reply section just the STATUS section
+                              stating that the zero, and optional summary, was done
+                              If Which='all', all normal cgminer and API statistics
+                              will be zeroed other than the numbers displayed by the
+                              usbstats and stats commands
+                              If Which='bestshare', only the 'Best Share' values
+                              are zeroed for each pool and the global 'Best Share'
+                              The true/false option determines if a full summary is
+                              shown on the cgminer display like is normally displayed
+                              on exit.
+
 When you enable, disable or restart a GPU or PGA, you will also get Thread messages
 When you enable, disable or restart a GPU or PGA, you will also get Thread messages
 in the cgminer status window
 in the cgminer status window
 
 
@@ -400,7 +412,18 @@ miner.php - an example web page to access the API
 Feature Changelog for external applications using the API:
 Feature Changelog for external applications using the API:
 
 
 
 
-API V1.23
+API V1.24
+
+Added API commands:
+ 'zero'
+
+Modified API commands:
+ 'pools' - add 'Best Share'
+ 'devs' and 'pga' - add 'No Device' for PGAs if MMQ or BFL compiled
+
+----------
+
+API V1.23 (cgminer v2.10.2)
 
 
 Added API commands:
 Added API commands:
  'pgaset' - with: MMQ opt=clock val=160 to 230 (and a multiple of 2)
  'pgaset' - with: MMQ opt=clock val=160 to 230 (and a multiple of 2)
@@ -930,6 +953,38 @@ true
 
 
 ---------
 ---------
 
 
+Default:
+ $userlist = null;
+
+Define password checking and default access
+ null means there is no password checking
+
+$userlist is an array of 3 arrays e.g.
+$userlist = array('sys' => array('boss' => 'bpass'),
+                  'usr' => array('user' => 'upass', 'pleb' => 'ppass'),
+                  'def' => array('Pools'));
+
+'sys' is an array of system users and passwords (full access)
+'usr' is an array of user level users and passwords (readonly access)
+'def' is an array of custompages that anyone not logged in can view
+
+Any of the 3 can be null, meaning there are none of that item
+
+All validated 'usr' users are given $readonly = true; access
+All validated 'sys' users are given the $readonly access you defined
+
+If 'def' has one or more values, and allowcustompages is true, then
+anyone without a password can see the list of custompage buttons given
+in 'def' and will see the first one when they go to the web page, with
+a login button at the top right
+
+From the login page, if you login with no username or password, it will
+show the first 'def' custompage (if there are any)
+
+If you are logged in, it will show a logout button at the top right
+
+---------
+
 Default:
 Default:
  $notify = true;
  $notify = true;
 
 
@@ -1302,9 +1357,12 @@ You can only see fields listed in 'group' and 'calc'
 
 
 A 'calc' is formatted as: 'Field' => 'function'
 A 'calc' is formatted as: 'Field' => 'function'
 The current list of operations available for 'calc' are:
 The current list of operations available for 'calc' are:
-'sum', 'avg', 'min', 'max', 'lo', 'hi', 'any'
+'sum', 'avg', 'min', 'max', 'lo', 'hi', 'coount', 'any'
 The first 4 are as expected - the numerical sum, average, minimum or maximum
 The first 4 are as expected - the numerical sum, average, minimum or maximum
 'lo' is the first string of the list, sorted ignoring case
 'lo' is the first string of the list, sorted ignoring case
 'hi' is the last string of the list, sorted ignoring case
 'hi' is the last string of the list, sorted ignoring case
+'count' is the number of rows in the section specified in the calc e.g.
+ ('DEVS.Name' => 'count') would be the number of DEVS selected in the 'where'
+ of course any valid 'DEVS.Xyz' would give the same 'count' value
 'any' is effectively random: the field value in the first row of the grouped data
 'any' is effectively random: the field value in the first row of the grouped data
 An unrecognised 'function' uses 'any'
 An unrecognised 'function' uses 'any'

BIN
API.class


+ 11 - 2
API.java

@@ -76,6 +76,7 @@ class API
 
 
 	public void process(String cmd, InetAddress ip, int port) throws Exception
 	public void process(String cmd, InetAddress ip, int port) throws Exception
 	{
 	{
+		StringBuffer sb = new StringBuffer();
 		char buf[] = new char[MAXRECEIVESIZE];
 		char buf[] = new char[MAXRECEIVESIZE];
 		int len = 0;
 		int len = 0;
 
 
@@ -89,7 +90,15 @@ System.out.println("Attempting to send '"+cmd+"' to "+ip.getHostAddress()+":"+po
 			ps.flush();
 			ps.flush();
 
 
 			InputStreamReader isr = new InputStreamReader(socket.getInputStream());
 			InputStreamReader isr = new InputStreamReader(socket.getInputStream());
-			len = isr.read(buf, 0, MAXRECEIVESIZE);
+			while (0x80085 > 0)
+			{
+				len = isr.read(buf, 0, MAXRECEIVESIZE);
+				if (len < 1)
+					break;
+				sb.append(buf, 0, len);
+				if (buf[len-1] == '\0')
+					break;
+			}
 
 
 			closeAll();
 			closeAll();
 		}
 		}
@@ -100,7 +109,7 @@ System.out.println("Attempting to send '"+cmd+"' to "+ip.getHostAddress()+":"+po
 			return;
 			return;
 		}
 		}
 
 
-		String result = new String(buf, 0, len);
+		String result = sb.toString();
 
 
 		System.out.println("Answer='"+result+"'");
 		System.out.println("Answer='"+result+"'");
 
 

+ 14 - 11
FPGA-README

@@ -2,15 +2,8 @@
 This README contains extended details about FPGA mining with cgminer
 This README contains extended details about FPGA mining with cgminer
 
 
 
 
-ModMinerQuad (MMQ)
-------------------
-
-The mining bitstream does not survive a power cycle, so cgminer will upload
-it, if it needs to, before it starts mining (approx 7min 40sec)
-
-The red LED also flashes while it is uploading the bitstream
-
--
+For ModMinerQuad (MMQ) and BitForce (BFL)
+-----------------------------------------
 
 
 When mining on windows, the driver being used will determine if mining will work.
 When mining on windows, the driver being used will determine if mining will work.
 
 
@@ -39,7 +32,17 @@ problems:
 
 
  --usb-dump 0
  --usb-dump 0
 
 
-It will only help if you have a working MMQ device attached to the computer
+It will only help if you have a working MMQ or BFL device attached to the
+computer
+
+
+ModMinerQuad (MMQ)
+------------------
+
+The mining bitstream does not survive a power cycle, so cgminer will upload
+it, if it needs to, before it starts mining (approx 7min 40sec)
+
+The red LED also flashes while it is uploading the bitstream
 
 
 -
 -
 
 
@@ -130,7 +133,7 @@ modem-manager software
 TODO: check that all MMQ's have the same product ID
 TODO: check that all MMQ's have the same product ID
 
 
 
 
-Bitforce (BFL)
+BitForce (BFL)
 --------------
 --------------
 
 
 --bfl-range         Use nonce range on bitforce devices if supported
 --bfl-range         Use nonce range on bitforce devices if supported

+ 5 - 1
Makefile.am

@@ -82,6 +82,10 @@ if NEED_FPGAUTILS
 cgminer_SOURCES += fpgautils.c fpgautils.h
 cgminer_SOURCES += fpgautils.c fpgautils.h
 endif
 endif
 
 
+if NEED_USBUTILS_C
+cgminer_SOURCES += usbutils.c
+endif
+
 if HAS_BITFORCE
 if HAS_BITFORCE
 cgminer_SOURCES += driver-bitforce.c
 cgminer_SOURCES += driver-bitforce.c
 endif
 endif
@@ -91,7 +95,7 @@ cgminer_SOURCES += driver-icarus.c
 endif
 endif
 
 
 if HAS_MODMINER
 if HAS_MODMINER
-cgminer_SOURCES += driver-modminer.c usbutils.c
+cgminer_SOURCES += driver-modminer.c
 bitstreamsdir = $(bindir)/bitstreams
 bitstreamsdir = $(bindir)/bitstreams
 dist_bitstreams_DATA = bitstreams/*
 dist_bitstreams_DATA = bitstreams/*
 endif
 endif

+ 11 - 9
README

@@ -220,15 +220,16 @@ SCRYPT only options:
 See SCRYPT-README for more information regarding litecoin mining.
 See SCRYPT-README for more information regarding litecoin mining.
 
 
 
 
-FPGA mining boards(BitForce, Icarus, ModMiner, Ztex) only options:
+FPGA mining boards (BitForce, Icarus, ModMiner, Ztex) only options:
 
 
-cgminer will automatically find your ModMiner or Ztex FPGAs
+cgminer will automatically find your ModMiner, BitForce or Ztex FPGAs
+independent of the --scan-serial options specified below
 
 
---scan-serial|-S <arg> Serial port to probe for FPGA mining device
+--scan-serial|-S <arg> Serial port to probe for Icarus mining device
 
 
-This option is only for BitForce and/or Icarus FPGAs
+This option is only for Icarus bitstream FPGAs
 
 
-By default, cgminer will scan for autodetected FPGAs unless at least one
+By default, cgminer will scan for autodetected Icarus unless at least one
 -S is specified for that driver. If you specify -S and still want cgminer
 -S is specified for that driver. If you specify -S and still want cgminer
 to scan, you must also use "-S auto". If you want to prevent cgminer from
 to scan, you must also use "-S auto". If you want to prevent cgminer from
 scanning without specifying a device, you can use "-S noauto". Note that
 scanning without specifying a device, you can use "-S noauto". Note that
@@ -237,13 +238,14 @@ device depending on the version of udev being used.
 
 
 On linux <arg> is usually of the format /dev/ttyUSBn
 On linux <arg> is usually of the format /dev/ttyUSBn
 On windows <arg> is usually of the format \\.\COMn
 On windows <arg> is usually of the format \\.\COMn
-(where n = the correct device number for the FPGA device)
+(where n = the correct device number for the Icarus device)
 
 
 The official supplied binaries are compiled with support for all FPGAs.
 The official supplied binaries are compiled with support for all FPGAs.
 To force the code to only attempt detection with a specific driver,
 To force the code to only attempt detection with a specific driver,
 prepend the argument with the driver name followed by a colon.
 prepend the argument with the driver name followed by a colon.
-For example, "icarus:/dev/ttyUSB0" or "bitforce:\\.\COM5"
-or using the short name: "ica:/dev/ttyUSB0" or "bfl:\\.\COM5"
+For example, "icarus:/dev/ttyUSB0" or using the short name: "ica:/dev/ttyUSB0"
+This option not longer matters since Icarus is the only serial-USB
+device that uses it
 
 
 For other FPGA details see the FPGA-README
 For other FPGA details see the FPGA-README
 
 
@@ -455,7 +457,7 @@ The block display shows:
 Block: 0074c5e482e34a506d2a051a...  Started: [17:17:22]  Best share: 2.71K
 Block: 0074c5e482e34a506d2a051a...  Started: [17:17:22]  Best share: 2.71K
 
 
 This shows a short stretch of the current block, when the new block started,
 This shows a short stretch of the current block, when the new block started,
-and the all time best difficulty share you've submitted since starting cgminer
+and the all time best difficulty share you've found since starting cgminer
 this time.
 this time.
 
 
 
 

+ 1 - 1
adl.c

@@ -1392,7 +1392,7 @@ updated:
 		clear_logwin();
 		clear_logwin();
 		return;
 		return;
 	}
 	}
-	sleep(1);
+	nmsleep(1000);
 	goto updated;
 	goto updated;
 }
 }
 #endif
 #endif

+ 153 - 62
api.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2011-2012 Andrew Smith
+ * Copyright 2011-2013 Andrew Smith
  * Copyright 2011-2012 Con Kolivas
  * Copyright 2011-2012 Con Kolivas
  *
  *
  * This program is free software; you can redistribute it and/or modify it
  * This program is free software; you can redistribute it and/or modify it
@@ -133,7 +133,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 static const char GPUSEP = ',';
 
 
-static const char *APIVERSION = "1.23";
+static const char *APIVERSION = "1.24";
 static const char *DEAD = "Dead";
 static const char *DEAD = "Dead";
 #if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
 #if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
 static const char *SICK = "Sick";
 static const char *SICK = "Sick";
@@ -387,6 +387,12 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_PGASETERR 93
 #define MSG_PGASETERR 93
 #endif
 #endif
 
 
+#define MSG_ZERMIS 94
+#define MSG_ZERINV 95
+#define MSG_ZERSUM 96
+#define MSG_ZERNOSUM 97
+#define MSG_USBNODEV 98
+
 enum code_severity {
 enum code_severity {
 	SEVERITY_ERR,
 	SEVERITY_ERR,
 	SEVERITY_WARN,
 	SEVERITY_WARN,
@@ -558,6 +564,13 @@ struct CODES {
  { SEVERITY_INFO,  MSG_PGAHELP, PARAM_BOTH,	"PGA %d set help: %s" },
  { SEVERITY_INFO,  MSG_PGAHELP, PARAM_BOTH,	"PGA %d set help: %s" },
  { SEVERITY_SUCC,  MSG_PGASETOK, PARAM_BOTH,	"PGA %d set OK" },
  { SEVERITY_SUCC,  MSG_PGASETOK, PARAM_BOTH,	"PGA %d set OK" },
  { SEVERITY_ERR,   MSG_PGASETERR, PARAM_BOTH,	"PGA %d set failed: %s" },
  { SEVERITY_ERR,   MSG_PGASETERR, PARAM_BOTH,	"PGA %d set failed: %s" },
+#endif
+ { SEVERITY_ERR,   MSG_ZERMIS,	PARAM_NONE,	"Missing zero parameters" },
+ { SEVERITY_ERR,   MSG_ZERINV,	PARAM_STR,	"Invalid zero parameter '%s'" },
+ { SEVERITY_SUCC,  MSG_ZERSUM,	PARAM_STR,	"Zeroed %s stats with summary" },
+ { SEVERITY_SUCC,  MSG_ZERNOSUM, PARAM_STR,	"Zeroed %s stats without summary" },
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
+ { SEVERITY_ERR,   MSG_USBNODEV, PARAM_PGA,	"PGA%d has no device" },
 #endif
 #endif
  { SEVERITY_FAIL, 0, 0, NULL }
  { SEVERITY_FAIL, 0, 0, NULL }
 };
 };
@@ -599,22 +612,6 @@ struct APIGROUPS {
 static struct IP4ACCESS *ipaccess = NULL;
 static struct IP4ACCESS *ipaccess = NULL;
 static int ips = 0;
 static int ips = 0;
 
 
-#ifdef USE_BITFORCE
-extern struct device_api bitforce_api;
-#endif
-
-#ifdef USE_ICARUS
-extern struct device_api icarus_api;
-#endif
-
-#ifdef USE_ZTEX
-extern struct device_api ztex_api;
-#endif
-
-#ifdef USE_MODMINER
-extern struct device_api modminer_api;
-#endif
-
 struct io_data {
 struct io_data {
 	size_t siz;
 	size_t siz;
 	char *ptr;
 	char *ptr;
@@ -1151,24 +1148,26 @@ static int numpgas()
 	int count = 0;
 	int count = 0;
 	int i;
 	int i;
 
 
+	mutex_lock(&devices_lock);
 	for (i = 0; i < total_devices; i++) {
 	for (i = 0; i < total_devices; i++) {
 #ifdef USE_BITFORCE
 #ifdef USE_BITFORCE
-		if (devices[i]->api == &bitforce_api)
+		if (devices[i]->drv->drv_id == DRIVER_BITFORCE)
 			count++;
 			count++;
 #endif
 #endif
 #ifdef USE_ICARUS
 #ifdef USE_ICARUS
-		if (devices[i]->api == &icarus_api)
+		if (devices[i]->drv->drv_id == DRIVER_ICARUS)
 			count++;
 			count++;
 #endif
 #endif
 #ifdef USE_ZTEX
 #ifdef USE_ZTEX
-		if (devices[i]->api == &ztex_api)
+		if (devices[i]->drv->drv_id == DRIVER_ZTEX)
 			count++;
 			count++;
 #endif
 #endif
 #ifdef USE_MODMINER
 #ifdef USE_MODMINER
-		if (devices[i]->api == &modminer_api)
+		if (devices[i]->drv->drv_id == DRIVER_MODMINER)
 			count++;
 			count++;
 #endif
 #endif
 	}
 	}
+	mutex_unlock(&devices_lock);
 	return count;
 	return count;
 }
 }
 
 
@@ -1177,27 +1176,35 @@ static int pgadevice(int pgaid)
 	int count = 0;
 	int count = 0;
 	int i;
 	int i;
 
 
+	mutex_lock(&devices_lock);
 	for (i = 0; i < total_devices; i++) {
 	for (i = 0; i < total_devices; i++) {
 #ifdef USE_BITFORCE
 #ifdef USE_BITFORCE
-		if (devices[i]->api == &bitforce_api)
+		if (devices[i]->drv->drv_id == DRIVER_BITFORCE)
 			count++;
 			count++;
 #endif
 #endif
 #ifdef USE_ICARUS
 #ifdef USE_ICARUS
-		if (devices[i]->api == &icarus_api)
+		if (devices[i]->drv->drv_id == DRIVER_ICARUS)
 			count++;
 			count++;
 #endif
 #endif
 #ifdef USE_ZTEX
 #ifdef USE_ZTEX
-		if (devices[i]->api == &ztex_api)
+		if (devices[i]->drv->drv_id == DRIVER_ZTEX)
 			count++;
 			count++;
 #endif
 #endif
 #ifdef USE_MODMINER
 #ifdef USE_MODMINER
-		if (devices[i]->api == &modminer_api)
+		if (devices[i]->drv->drv_id == DRIVER_MODMINER)
 			count++;
 			count++;
 #endif
 #endif
 		if (count == (pgaid + 1))
 		if (count == (pgaid + 1))
-			return i;
+			goto foundit;
 	}
 	}
+
+	mutex_unlock(&devices_lock);
 	return -1;
 	return -1;
+
+foundit:
+
+	mutex_unlock(&devices_lock);
+	return i;
 }
 }
 #endif
 #endif
 
 
@@ -1530,16 +1537,16 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom
 		if (dev < 0) // Should never happen
 		if (dev < 0) // Should never happen
 			return;
 			return;
 
 
-		struct cgpu_info *cgpu = devices[dev];
+		struct cgpu_info *cgpu = get_devices(dev);
 		double frequency = 0;
 		double frequency = 0;
 		float temp = cgpu->temp;
 		float temp = cgpu->temp;
 
 
 #ifdef USE_ZTEX
 #ifdef USE_ZTEX
-		if (cgpu->api == &ztex_api && cgpu->device_ztex)
+		if (cgpu->drv->drv_id == DRIVER_ZTEX && cgpu->device_ztex)
 			frequency = cgpu->device_ztex->freqM1 * (cgpu->device_ztex->freqM + 1);
 			frequency = cgpu->device_ztex->freqM1 * (cgpu->device_ztex->freqM + 1);
 #endif
 #endif
 #ifdef USE_MODMINER
 #ifdef USE_MODMINER
-		if (cgpu->api == &modminer_api)
+		if (cgpu->drv->drv_id == DRIVER_MODMINER)
 			frequency = cgpu->clock;
 			frequency = cgpu->clock;
 #endif
 #endif
 
 
@@ -1553,7 +1560,7 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom
 		status = (char *)status2str(cgpu->status);
 		status = (char *)status2str(cgpu->status);
 
 
 		root = api_add_int(root, "PGA", &pga, false);
 		root = api_add_int(root, "PGA", &pga, false);
-		root = api_add_string(root, "Name", cgpu->api->name, false);
+		root = api_add_string(root, "Name", cgpu->drv->name, false);
 		root = api_add_int(root, "ID", &(cgpu->device_id), false);
 		root = api_add_int(root, "ID", &(cgpu->device_id), false);
 		root = api_add_string(root, "Enabled", enabled, false);
 		root = api_add_string(root, "Enabled", enabled, false);
 		root = api_add_string(root, "Status", status, false);
 		root = api_add_string(root, "Status", status, false);
@@ -1577,6 +1584,9 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom
 		root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false);
 		root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false);
 		root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false);
 		root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false);
 		root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false);
 		root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false);
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
+		root = api_add_bool(root, "No Device", &(cgpu->usbinfo.nodev), false);
+#endif
 
 
 		root = print_data(root, buf, isjson, precom);
 		root = print_data(root, buf, isjson, precom);
 		io_add(io_data, buf);
 		io_add(io_data, buf);
@@ -1747,6 +1757,7 @@ static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *p
 
 
 static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 {
+	struct cgpu_info *cgpu;
 	int numpga = numpgas();
 	int numpga = numpgas();
 	struct thr_info *thr;
 	struct thr_info *thr;
 	int pga;
 	int pga;
@@ -1775,7 +1786,10 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 		return;
 		return;
 	}
 	}
 
 
-	struct cgpu_info *cgpu = devices[dev];
+	cgpu = get_devices(dev);
+
+	applog(LOG_DEBUG, "API: request to pgaenable pgaid %d device %d %s%u",
+			id, dev, cgpu->drv->name, cgpu->device_id);
 
 
 	if (cgpu->deven != DEV_DISABLED) {
 	if (cgpu->deven != DEV_DISABLED) {
 		message(io_data, MSG_PGALRENA, id, NULL, isjson);
 		message(io_data, MSG_PGALRENA, id, NULL, isjson);
@@ -1789,11 +1803,19 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 	}
 	}
 #endif
 #endif
 
 
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
+	if (cgpu->usbinfo.nodev) {
+		message(io_data, MSG_USBNODEV, id, NULL, isjson);
+		return;
+	}
+#endif
+
 	for (i = 0; i < mining_threads; i++) {
 	for (i = 0; i < mining_threads; i++) {
-		pga = thr_info[i].cgpu->device_id;
+		thr = get_thread(i);
+		pga = thr->cgpu->cgminer_id;
 		if (pga == dev) {
 		if (pga == dev) {
-			thr = &thr_info[i];
 			cgpu->deven = DEV_ENABLED;
 			cgpu->deven = DEV_ENABLED;
+			applog(LOG_DEBUG, "API: pushing ping (%d) to thread %d", ping, thr->id);
 			tq_push(thr->q, &ping);
 			tq_push(thr->q, &ping);
 		}
 		}
 	}
 	}
@@ -1803,6 +1825,7 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 
 
 static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 {
+	struct cgpu_info *cgpu;
 	int numpga = numpgas();
 	int numpga = numpgas();
 	int id;
 	int id;
 
 
@@ -1828,7 +1851,10 @@ static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha
 		return;
 		return;
 	}
 	}
 
 
-	struct cgpu_info *cgpu = devices[dev];
+	cgpu = get_devices(dev);
+
+	applog(LOG_DEBUG, "API: request to pgadisable pgaid %d device %d %s%u",
+			id, dev, cgpu->drv->name, cgpu->device_id);
 
 
 	if (cgpu->deven == DEV_DISABLED) {
 	if (cgpu->deven == DEV_DISABLED) {
 		message(io_data, MSG_PGALRDIS, id, NULL, isjson);
 		message(io_data, MSG_PGALRDIS, id, NULL, isjson);
@@ -1842,6 +1868,8 @@ static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha
 
 
 static void pgaidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 static void pgaidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 {
+	struct cgpu_info *cgpu;
+	struct device_drv *drv;
 	int numpga = numpgas();
 	int numpga = numpgas();
 	int id;
 	int id;
 
 
@@ -1867,13 +1895,13 @@ static void pgaidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, ch
 		return;
 		return;
 	}
 	}
 
 
-	struct cgpu_info *cgpu = devices[dev];
-	struct device_api *api = cgpu->api;
+	cgpu = get_devices(dev);
+	drv = cgpu->drv;
 
 
-	if (!api->identify_device)
+	if (!drv->identify_device)
 		message(io_data, MSG_PGANOID, id, NULL, isjson);
 		message(io_data, MSG_PGANOID, id, NULL, isjson);
 	else {
 	else {
-		api->identify_device(cgpu);
+		drv->identify_device(cgpu);
 		message(io_data, MSG_PGAIDENT, id, NULL, isjson);
 		message(io_data, MSG_PGAIDENT, id, NULL, isjson);
 	}
 	}
 }
 }
@@ -1993,6 +2021,7 @@ static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m
 		else
 		else
 			root = api_add_const(root, "Stratum URL", BLANK, false);
 			root = api_add_const(root, "Stratum URL", BLANK, false);
 		root = api_add_bool(root, "Has GBT", &(pool->has_gbt), false);
 		root = api_add_bool(root, "Has GBT", &(pool->has_gbt), false);
+		root = api_add_uint64(root, "Best Share", &(pool->best_diff), true);
 
 
 		root = print_data(root, buf, isjson, isjson && (i > 0));
 		root = print_data(root, buf, isjson, isjson && (i > 0));
 		io_add(io_data, buf);
 		io_add(io_data, buf);
@@ -2081,23 +2110,25 @@ static void gpuenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 		return;
 		return;
 	}
 	}
 
 
+	applog(LOG_DEBUG, "API: request to gpuenable gpuid %d %s%u",
+			id, gpus[id].drv->name, gpus[id].device_id);
+
 	if (gpus[id].deven != DEV_DISABLED) {
 	if (gpus[id].deven != DEV_DISABLED) {
 		message(io_data, MSG_ALRENA, id, NULL, isjson);
 		message(io_data, MSG_ALRENA, id, NULL, isjson);
 		return;
 		return;
 	}
 	}
 
 
 	for (i = 0; i < gpu_threads; i++) {
 	for (i = 0; i < gpu_threads; i++) {
-		gpu = thr_info[i].cgpu->device_id;
+		thr = get_thread(i);
+		gpu = thr->cgpu->device_id;
 		if (gpu == id) {
 		if (gpu == id) {
-			thr = &thr_info[i];
 			if (thr->cgpu->status != LIFE_WELL) {
 			if (thr->cgpu->status != LIFE_WELL) {
 				message(io_data, MSG_GPUMRE, id, NULL, isjson);
 				message(io_data, MSG_GPUMRE, id, NULL, isjson);
 				return;
 				return;
 			}
 			}
-
 			gpus[id].deven = DEV_ENABLED;
 			gpus[id].deven = DEV_ENABLED;
+			applog(LOG_DEBUG, "API: pushing ping (%d) to thread %d", ping, thr->id);
 			tq_push(thr->q, &ping);
 			tq_push(thr->q, &ping);
-
 		}
 		}
 	}
 	}
 
 
@@ -2124,6 +2155,9 @@ static void gpudisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha
 		return;
 		return;
 	}
 	}
 
 
+	applog(LOG_DEBUG, "API: request to gpudisable gpuid %d %s%u",
+			id, gpus[id].drv->name, gpus[id].device_id);
+
 	if (gpus[id].deven == DEV_DISABLED) {
 	if (gpus[id].deven == DEV_DISABLED) {
 		message(io_data, MSG_ALRDIS, id, NULL, isjson);
 		message(io_data, MSG_ALRDIS, id, NULL, isjson);
 		return;
 		return;
@@ -2752,7 +2786,7 @@ void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, b
 	// ALL counters (and only counters) must start the name with a '*'
 	// ALL counters (and only counters) must start the name with a '*'
 	// Simplifies future external support for identifying new counters
 	// Simplifies future external support for identifying new counters
 	root = api_add_int(root, "NOTIFY", &device, false);
 	root = api_add_int(root, "NOTIFY", &device, false);
-	root = api_add_string(root, "Name", cgpu->api->name, false);
+	root = api_add_string(root, "Name", cgpu->drv->name, false);
 	root = api_add_int(root, "ID", &(cgpu->device_id), false);
 	root = api_add_int(root, "ID", &(cgpu->device_id), false);
 	root = api_add_time(root, "Last Well", &(cgpu->device_last_well), false);
 	root = api_add_time(root, "Last Well", &(cgpu->device_last_well), false);
 	root = api_add_time(root, "Last Not Well", &(cgpu->device_last_not_well), false);
 	root = api_add_time(root, "Last Not Well", &(cgpu->device_last_not_well), false);
@@ -2774,6 +2808,7 @@ void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, b
 
 
 static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, char group)
 static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, char group)
 {
 {
+	struct cgpu_info *cgpu;
 	bool io_open = false;
 	bool io_open = false;
 	int i;
 	int i;
 
 
@@ -2787,8 +2822,10 @@ static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe
 	if (isjson)
 	if (isjson)
 		io_open = io_add(io_data, COMSTR JSON_NOTIFY);
 		io_open = io_add(io_data, COMSTR JSON_NOTIFY);
 
 
-	for (i = 0; i < total_devices; i++)
-		notifystatus(io_data, i, devices[i], isjson, group);
+	for (i = 0; i < total_devices; i++) {
+		cgpu = get_devices(i);
+		notifystatus(io_data, i, cgpu, isjson, group);
+	}
 
 
 	if (isjson && io_open)
 	if (isjson && io_open)
 		io_close(io_data);
 		io_close(io_data);
@@ -2813,12 +2850,12 @@ static void devdetails(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m
 		io_open = io_add(io_data, COMSTR JSON_DEVDETAILS);
 		io_open = io_add(io_data, COMSTR JSON_DEVDETAILS);
 
 
 	for (i = 0; i < total_devices; i++) {
 	for (i = 0; i < total_devices; i++) {
-		cgpu = devices[i];
+		cgpu = get_devices(i);
 
 
 		root = api_add_int(root, "DEVDETAILS", &i, false);
 		root = api_add_int(root, "DEVDETAILS", &i, false);
-		root = api_add_string(root, "Name", cgpu->api->name, false);
+		root = api_add_string(root, "Name", cgpu->drv->name, false);
 		root = api_add_int(root, "ID", &(cgpu->device_id), false);
 		root = api_add_int(root, "ID", &(cgpu->device_id), false);
-		root = api_add_string(root, "Driver", cgpu->api->dname, false);
+		root = api_add_string(root, "Driver", cgpu->drv->dname, false);
 		root = api_add_const(root, "Kernel", cgpu->kname ? : BLANK, false);
 		root = api_add_const(root, "Kernel", cgpu->kname ? : BLANK, false);
 		root = api_add_const(root, "Model", cgpu->name ? : BLANK, false);
 		root = api_add_const(root, "Model", cgpu->name ? : BLANK, false);
 		root = api_add_const(root, "Device Path", cgpu->device_path ? : BLANK, false);
 		root = api_add_const(root, "Device Path", cgpu->device_path ? : BLANK, false);
@@ -2895,6 +2932,8 @@ static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_st
 		root = api_add_uint64(root, "Bytes Sent", &(pool_stats->bytes_sent), false);
 		root = api_add_uint64(root, "Bytes Sent", &(pool_stats->bytes_sent), false);
 		root = api_add_uint64(root, "Times Recv", &(pool_stats->times_received), false);
 		root = api_add_uint64(root, "Times Recv", &(pool_stats->times_received), false);
 		root = api_add_uint64(root, "Bytes Recv", &(pool_stats->bytes_received), false);
 		root = api_add_uint64(root, "Bytes Recv", &(pool_stats->bytes_received), false);
+		root = api_add_uint64(root, "Net Bytes Sent", &(pool_stats->net_bytes_sent), false);
+		root = api_add_uint64(root, "Net Bytes Recv", &(pool_stats->net_bytes_received), false);
 	}
 	}
 
 
 	if (extra)
 	if (extra)
@@ -2908,6 +2947,7 @@ static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_st
 
 
 static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
 static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
 {
 {
+	struct cgpu_info *cgpu;
 	bool io_open = false;
 	bool io_open = false;
 	struct api_data *extra;
 	struct api_data *extra;
 	char id[20];
 	char id[20];
@@ -2920,15 +2960,15 @@ static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m
 
 
 	i = 0;
 	i = 0;
 	for (j = 0; j < total_devices; j++) {
 	for (j = 0; j < total_devices; j++) {
-		struct cgpu_info *cgpu = devices[j];
+		cgpu = get_devices(j);
 
 
-		if (cgpu && cgpu->api) {
-			if (cgpu->api->get_api_stats)
-				extra = cgpu->api->get_api_stats(cgpu);
+		if (cgpu && cgpu->drv) {
+			if (cgpu->drv->get_api_stats)
+				extra = cgpu->drv->get_api_stats(cgpu);
 			else
 			else
 				extra = NULL;
 				extra = NULL;
 
 
-			sprintf(id, "%s%d", cgpu->api->name, cgpu->device_id);
+			sprintf(id, "%s%d", cgpu->drv->name, cgpu->device_id);
 			i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, isjson);
 			i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, isjson);
 		}
 		}
 	}
 	}
@@ -3120,7 +3160,7 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may
 {
 {
 	struct api_data *root = NULL;
 	struct api_data *root = NULL;
 
 
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 	char buf[TMPBUFSIZ];
 	char buf[TMPBUFSIZ];
 	bool io_open = false;
 	bool io_open = false;
 	int count = 0;
 	int count = 0;
@@ -3133,7 +3173,7 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may
 		return;
 		return;
 	}
 	}
 
 
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 
 
 	message(io_data, MSG_USBSTA, 0, NULL, isjson);
 	message(io_data, MSG_USBSTA, 0, NULL, isjson);
 
 
@@ -3160,6 +3200,8 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may
 #ifdef HAVE_AN_FPGA
 #ifdef HAVE_AN_FPGA
 static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
 static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
 {
 {
+	struct cgpu_info *cgpu;
+	struct device_drv *drv;
 	char buf[TMPBUFSIZ];
 	char buf[TMPBUFSIZ];
 	int numpga = numpgas();
 	int numpga = numpgas();
 
 
@@ -3193,17 +3235,17 @@ static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe
 		return;
 		return;
 	}
 	}
 
 
-	struct cgpu_info *cgpu = devices[dev];
-	struct device_api *api = cgpu->api;
+	cgpu = get_devices(dev);
+	drv = cgpu->drv;
 
 
 	char *set = strchr(opt, ',');
 	char *set = strchr(opt, ',');
 	if (set)
 	if (set)
 		*(set++) = '\0';
 		*(set++) = '\0';
 
 
-	if (!api->set_device)
+	if (!drv->set_device)
 		message(io_data, MSG_PGANOSET, id, NULL, isjson);
 		message(io_data, MSG_PGANOSET, id, NULL, isjson);
 	else {
 	else {
-		char *ret = api->set_device(cgpu, opt, set, buf);
+		char *ret = drv->set_device(cgpu, opt, set, buf);
 		if (ret) {
 		if (ret) {
 			if (strcasecmp(opt, "help") == 0)
 			if (strcasecmp(opt, "help") == 0)
 				message(io_data, MSG_PGAHELP, id, ret, isjson);
 				message(io_data, MSG_PGAHELP, id, ret, isjson);
@@ -3215,6 +3257,54 @@ static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe
 }
 }
 #endif
 #endif
 
 
+static void dozero(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_ZERMIS, 0, NULL, isjson);
+		return;
+	}
+
+	char *sum = strchr(param, ',');
+	if (sum)
+		*(sum++) = '\0';
+	if (!sum || !*sum) {
+		message(io_data, MSG_MISBOOL, 0, NULL, isjson);
+		return;
+	}
+
+	bool all = false;
+	bool bs = false;
+	if (strcasecmp(param, "all") == 0)
+		all = true;
+	else if (strcasecmp(param, "bestshare") == 0)
+		bs = true;
+
+	if (all == false && bs == false) {
+		message(io_data, MSG_ZERINV, 0, param, isjson);
+		return;
+	}
+
+	*sum = tolower(*sum);
+	if (*sum != 't' && *sum != 'f') {
+		message(io_data, MSG_INVBOOL, 0, NULL, isjson);
+		return;
+	}
+
+	bool dosum = (*sum == 't');
+	if (dosum)
+		print_summary();
+
+	if (all)
+		zero_stats();
+	if (bs)
+		zero_bestshare();
+
+	if (dosum)
+		message(io_data, MSG_ZERSUM, 0, all ? "All" : "BestShare", isjson);
+	else
+		message(io_data, MSG_ZERNOSUM, 0, all ? "All" : "BestShare", isjson);
+}
+
 static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group);
 static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group);
 
 
 struct CMDS {
 struct CMDS {
@@ -3274,6 +3364,7 @@ struct CMDS {
 #ifdef HAVE_AN_FPGA
 #ifdef HAVE_AN_FPGA
 	{ "pgaset",		pgaset,		true },
 	{ "pgaset",		pgaset,		true },
 #endif
 #endif
+	{ "zero",		dozero,		true },
 	{ NULL,			NULL,		false }
 	{ NULL,			NULL,		false }
 };
 };
 
 
@@ -3701,7 +3792,7 @@ void api(int api_thr_id)
 
 
 	/* This should be done before curl in needed
 	/* This should be done before curl in needed
 	 * to ensure curl has already called WSAStartup() in windows */
 	 * to ensure curl has already called WSAStartup() in windows */
-	sleep(opt_log_interval);
+	nmsleep(opt_log_interval*1000);
 
 
 	sock = socket(AF_INET, SOCK_STREAM, 0);
 	sock = socket(AF_INET, SOCK_STREAM, 0);
 	if (sock == INVSOCK) {
 	if (sock == INVSOCK) {
@@ -3747,7 +3838,7 @@ void api(int api_thr_id)
 				break;
 				break;
 			else {
 			else {
 				applog(LOG_WARNING, "API bind to port %d failed - trying again in 30sec", port);
 				applog(LOG_WARNING, "API bind to port %d failed - trying again in 30sec", port);
-				sleep(30);
+				nmsleep(30000);
 			}
 			}
 		} else
 		} else
 			bound = 1;
 			bound = 1;

File diff suppressed because it is too large
+ 443 - 141
cgminer.c


+ 4 - 3
configure.ac

@@ -273,6 +273,7 @@ fi
 
 
 
 
 AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$ztex != xnononono])
 AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$ztex != xnononono])
+AM_CONDITIONAL([NEED_USBUTILS_C], [test x$bitforce$modminer != xnono])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue])
 AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue])
 AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue])
 AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue])
@@ -321,7 +322,7 @@ fi
 
 
 AM_CONDITIONAL([HAS_YASM], [test x$has_yasm = xtrue])
 AM_CONDITIONAL([HAS_YASM], [test x$has_yasm = xtrue])
 
 
-if test "x$bitforce" != xno; then
+if test "x$icarus" != xno; then
 	AC_ARG_WITH([libudev], [AC_HELP_STRING([--without-libudev], [Autodetect FPGAs using libudev (default enabled)])],
 	AC_ARG_WITH([libudev], [AC_HELP_STRING([--without-libudev], [Autodetect FPGAs using libudev (default enabled)])],
 		[libudev=$withval],
 		[libudev=$withval],
 		[libudev=auto]
 		[libudev=auto]
@@ -343,7 +344,7 @@ AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno])
 
 
 PKG_PROG_PKG_CONFIG()
 PKG_PROG_PKG_CONFIG()
 
 
-if test "x$ztex$modminer" != xnono; then
+if test "x$ztex$modminer$bitforce" != xnonono; then
   case $target in
   case $target in
     *-*-freebsd*)
     *-*-freebsd*)
       LIBUSB_LIBS="-lusb"
       LIBUSB_LIBS="-lusb"
@@ -508,7 +509,7 @@ else
 	echo "  Ztex.FPGAs...........: Disabled"
 	echo "  Ztex.FPGAs...........: Disabled"
 fi
 fi
 
 
-if test "x$bitforce" != xno; then
+if test "x$icarus" != xno; then
 	echo "  libudev.detection....: $libudev"
 	echo "  libudev.detection....: $libudev"
 fi
 fi
 
 

+ 359 - 321
driver-bitforce.c

@@ -1,4 +1,5 @@
 /*
 /*
+ * Copyright 2012-2013 Andrew Smith
  * Copyright 2012 Luke Dashjr
  * Copyright 2012 Luke Dashjr
  * Copyright 2012 Con Kolivas
  * Copyright 2012 Con Kolivas
  *
  *
@@ -19,34 +20,35 @@
 #include "config.h"
 #include "config.h"
 
 
 #ifdef WIN32
 #ifdef WIN32
-
 #include <windows.h>
 #include <windows.h>
-
-#define dlsym (void*)GetProcAddress
-#define dlclose FreeLibrary
-
-typedef unsigned long FT_STATUS;
-typedef PVOID FT_HANDLE;
-__stdcall FT_STATUS (*FT_ListDevices)(PVOID pArg1, PVOID pArg2, DWORD Flags);
-__stdcall FT_STATUS (*FT_Open)(int idx, FT_HANDLE*);
-__stdcall FT_STATUS (*FT_GetComPortNumber)(FT_HANDLE, LPLONG lplComPortNumber);
-__stdcall FT_STATUS (*FT_Close)(FT_HANDLE);
-const uint32_t FT_OPEN_BY_DESCRIPTION =       2;
-const uint32_t FT_LIST_ALL         = 0x20000000;
-const uint32_t FT_LIST_NUMBER_ONLY = 0x80000000;
-enum {
-	FT_OK,
-};
-
-// Code must deal with a timeout. Make it 1 second on windows, 0.1 on linux.
-#define BFopen(devpath)  serial_open(devpath, 0, 10, true)
-#else /* WIN32 */
-#define BFopen(devpath)  serial_open(devpath, 0, 1, true)
 #endif /* WIN32 */
 #endif /* WIN32 */
 
 
 #include "compat.h"
 #include "compat.h"
 #include "miner.h"
 #include "miner.h"
-#include "fpgautils.h"
+#include "usbutils.h"
+
+#define BITFORCE_IDENTIFY "ZGX"
+#define BITFORCE_IDENTIFY_LEN (sizeof(BITFORCE_IDENTIFY)-1)
+#define BITFORCE_FLASH "ZMX"
+#define BITFORCE_FLASH_LEN (sizeof(BITFORCE_FLASH)-1)
+#define BITFORCE_TEMPERATURE "ZLX"
+#define BITFORCE_TEMPERATURE_LEN (sizeof(BITFORCE_TEMPERATURE)-1)
+#define BITFORCE_SENDRANGE "ZPX"
+#define BITFORCE_SENDRANGE_LEN (sizeof(BITFORCE_SENDRANGE)-1)
+#define BITFORCE_SENDWORK "ZDX"
+#define BITFORCE_SENDWORK_LEN (sizeof(BITFORCE_SENDWORK)-1)
+#define BITFORCE_WORKSTATUS "ZFX"
+#define BITFORCE_WORKSTATUS_LEN (sizeof(BITFORCE_WORKSTATUS)-1)
+
+// Either of Nonce or No-nonce start with:
+#define BITFORCE_EITHER "N"
+#define BITFORCE_EITHER_LEN 1
+#define BITFORCE_NONCE "NONCE-FOUND"
+#define BITFORCE_NONCE_LEN (sizeof(BITFORCE_NONCE)-1)
+#define BITFORCE_NO_NONCE "NO-NONCE"
+#define BITFORCE_NO_NONCE_MATCH 3
+#define BITFORCE_IDLE "IDLE"
+#define BITFORCE_IDLE_MATCH 1
 
 
 #define BITFORCE_SLEEP_MS 500
 #define BITFORCE_SLEEP_MS 500
 #define BITFORCE_TIMEOUT_S 7
 #define BITFORCE_TIMEOUT_S 7
@@ -62,62 +64,197 @@ enum {
 #define KNAME_WORK  "full work"
 #define KNAME_WORK  "full work"
 #define KNAME_RANGE "nonce range"
 #define KNAME_RANGE "nonce range"
 
 
-struct device_api bitforce_api;
+#define BITFORCE_BUFSIZ (0x200)
 
 
-static void BFgets(char *buf, size_t bufLen, int fd)
-{
-	do {
-		buf[0] = '\0';
-		--bufLen;
-	} while (likely(bufLen && read(fd, buf, 1) == 1 && (buf++)[0] != '\n'));
+// If initialisation fails the first time,
+// sleep this amount (ms) and try again
+#define REINIT_TIME_FIRST_MS 100
+// Max ms per sleep
+#define REINIT_TIME_MAX_MS 800
+// Keep trying up to this many us
+#define REINIT_TIME_MAX 3000000
 
 
-	buf[0] = '\0';
-}
+static const char *blank = "";
+
+struct device_drv bitforce_drv;
 
 
-static ssize_t BFwrite(int fd, const void *buf, ssize_t bufLen)
+static void bitforce_initialise(struct cgpu_info *bitforce, bool lock)
 {
 {
-	if ((bufLen) != write(fd, buf, bufLen))
-		return 0;
-	else
-		return bufLen;
-}
+	int err;
 
 
-#define BFclose(fd) close(fd)
+	if (lock)
+		mutex_lock(&bitforce->device_mutex);
 
 
-static bool bitforce_detect_one(const char *devpath)
+	// Reset
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET,
+				FTDI_VALUE_RESET, bitforce->usbdev->found->interface, C_RESET);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: reset got err %d",
+			bitforce->drv->name, bitforce->device_id, err);
+
+	if (bitforce->usbinfo.nodev)
+		goto failed;
+
+	// Set data control
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_DATA,
+				FTDI_VALUE_DATA, bitforce->usbdev->found->interface, C_SETDATA);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: setdata got err %d",
+			bitforce->drv->name, bitforce->device_id, err);
+
+	if (bitforce->usbinfo.nodev)
+		goto failed;
+
+	// Set the baud
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD,
+				(FTDI_INDEX_BAUD & 0xff00) | bitforce->usbdev->found->interface,
+				C_SETBAUD);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: setbaud got err %d",
+			bitforce->drv->name, bitforce->device_id, err);
+
+	if (bitforce->usbinfo.nodev)
+		goto failed;
+
+	// Set Flow Control
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW,
+				FTDI_VALUE_FLOW, bitforce->usbdev->found->interface, C_SETFLOW);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: setflowctrl got err %d",
+			bitforce->drv->name, bitforce->device_id, err);
+
+	if (bitforce->usbinfo.nodev)
+		goto failed;
+
+	// Set Modem Control
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM,
+				FTDI_VALUE_MODEM, bitforce->usbdev->found->interface, C_SETMODEM);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d",
+			bitforce->drv->name, bitforce->device_id, err);
+
+	if (bitforce->usbinfo.nodev)
+		goto failed;
+
+	// Clear any sent data
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET,
+				FTDI_VALUE_PURGE_TX, bitforce->usbdev->found->interface, C_PURGETX);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: purgetx got err %d",
+			bitforce->drv->name, bitforce->device_id, err);
+
+	if (bitforce->usbinfo.nodev)
+		goto failed;
+
+	// Clear any received data
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET,
+				FTDI_VALUE_PURGE_RX, bitforce->usbdev->found->interface, C_PURGERX);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: purgerx got err %d",
+			bitforce->drv->name, bitforce->device_id, err);
+
+failed:
+
+	if (lock)
+		mutex_unlock(&bitforce->device_mutex);
+}
+
+static bool bitforce_detect_one(struct libusb_device *dev, struct usb_find_devices *found)
 {
 {
-	int fdDev = BFopen(devpath);
-	struct cgpu_info *bitforce;
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	char devpath[20];
+	int err, amount;
 	char *s;
 	char *s;
+	struct timeval init_start, init_now;
+	int init_sleep, init_count;
+	bool ident_first;
 
 
-	applog(LOG_DEBUG, "BFL: Attempting to open %s", devpath);
+	struct cgpu_info *bitforce = NULL;
+	bitforce = calloc(1, sizeof(*bitforce));
+	bitforce->drv = &bitforce_drv;
+	bitforce->deven = DEV_ENABLED;
+	bitforce->threads = 1;
 
 
-	if (unlikely(fdDev == -1)) {
-		applog(LOG_ERR, "BFL: Failed to open %s", devpath);
-		return false;
+	if (!usb_init(bitforce, dev, found)) {
+		applog(LOG_ERR, "%s detect (%d:%d) failed to initialise (incorrect device?)",
+			bitforce->drv->dname,
+			(int)(bitforce->usbinfo.bus_number),
+			(int)(bitforce->usbinfo.device_address));
+		goto shin;
 	}
 	}
 
 
-	BFwrite(fdDev, "ZGX", 3);
-	pdevbuf[0] = '\0';
-	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "BFL: Error reading/timeout (ZGX)");
-		return 0;
+	sprintf(devpath, "%d:%d",
+			(int)(bitforce->usbinfo.bus_number),
+			(int)(bitforce->usbinfo.device_address));
+
+
+	// Allow 2 complete attempts if the 1st time returns an unrecognised reply
+	ident_first = true;
+retry:
+	init_count = 0;
+	init_sleep = REINIT_TIME_FIRST_MS;
+	gettimeofday(&init_start, NULL);
+reinit:
+	bitforce_initialise(bitforce, false);
+	if ((err = usb_write(bitforce, BITFORCE_IDENTIFY, BITFORCE_IDENTIFY_LEN, &amount, C_REQUESTIDENTIFY)) < 0 || amount != BITFORCE_IDENTIFY_LEN) {
+		applog(LOG_ERR, "%s detect (%s) send identify request failed (%d:%d)",
+			bitforce->drv->dname, devpath, amount, err);
+		goto unshin;
 	}
 	}
 
 
-	BFclose(fdDev);
-	if (unlikely(!strstr(pdevbuf, "SHA256"))) {
-		applog(LOG_ERR, "BFL: Didn't recognise BitForce on %s", devpath);
-		return false;
+	if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETIDENTIFY)) < 0 || amount < 1) {
+		init_count++;
+		gettimeofday(&init_now, NULL);
+		if (us_tdiff(&init_now, &init_start) <= REINIT_TIME_MAX) {
+			if (init_count == 2) {
+				applog(LOG_WARNING, "%s detect (%s) 2nd init failed (%d:%d) - retrying",
+					bitforce->drv->dname, devpath, amount, err);
+			}
+			nmsleep(init_sleep);
+			if ((init_sleep * 2) <= REINIT_TIME_MAX_MS)
+				init_sleep *= 2;
+			goto reinit;
+		}
+
+		if (init_count > 0)
+			applog(LOG_WARNING, "%s detect (%s) init failed %d times %.2fs",
+				bitforce->drv->dname, devpath, init_count, tdiff(&init_now, &init_start));
+
+		if (err < 0) {
+			applog(LOG_ERR, "%s detect (%s) error identify reply (%d:%d)",
+				bitforce->drv->dname, devpath, amount, err);
+		} else {
+			applog(LOG_ERR, "%s detect (%s) empty identify reply (%d)",
+				bitforce->drv->dname, devpath, amount);
+		}
+
+		goto unshin;
+	}
+	buf[amount] = '\0';
+
+	if (unlikely(!strstr(buf, "SHA256"))) {
+		if (ident_first) {
+			applog(LOG_WARNING, "%s detect (%s) didn't recognise '%s' trying again ...",
+				bitforce->drv->dname, devpath, buf);
+			ident_first = false;
+			goto retry;
+		}
+		applog(LOG_ERR, "%s detect (%s) didn't recognise '%s' on 2nd attempt",
+			bitforce->drv->dname, devpath, buf);
+		goto unshin;
+	}
+
+	if (likely((!memcmp(buf, ">>>ID: ", 7)) && (s = strstr(buf + 3, ">>>")))) {
+		s[0] = '\0';
+		bitforce->name = strdup(buf + 7);
+	} else {
+		bitforce->name = (char *)blank;
 	}
 	}
 
 
 	// We have a real BitForce!
 	// We have a real BitForce!
-	bitforce = calloc(1, sizeof(*bitforce));
-	bitforce->api = &bitforce_api;
-	bitforce->device_path = strdup(devpath);
-	bitforce->deven = DEV_ENABLED;
-	bitforce->threads = 1;
+	applog(LOG_DEBUG, "%s (%s) identified as: '%s'",
+		bitforce->drv->dname, devpath, bitforce->name);
+
 	/* Initially enable support for nonce range and disable it later if it
 	/* Initially enable support for nonce range and disable it later if it
 	 * fails */
 	 * fails */
 	if (opt_bfl_noncerange) {
 	if (opt_bfl_noncerange) {
@@ -129,108 +266,39 @@ static bool bitforce_detect_one(const char *devpath)
 		bitforce->kname = KNAME_WORK;
 		bitforce->kname = KNAME_WORK;
 	}
 	}
 
 
-	if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>")))) {
-		s[0] = '\0';
-		bitforce->name = strdup(pdevbuf + 7);
-	}
+	bitforce->device_path = strdup(devpath);
+
+	if (!add_cgpu(bitforce))
+		goto unshin;
+
+	update_usb_stats(bitforce);
 
 
 	mutex_init(&bitforce->device_mutex);
 	mutex_init(&bitforce->device_mutex);
 
 
-	return add_cgpu(bitforce);
-}
+	return true;
 
 
-#define LOAD_SYM(sym)  do { \
-	if (!(sym = dlsym(dll, #sym))) {  \
-		applog(LOG_DEBUG, "Failed to load " #sym ", not using FTDI bitforce autodetect");  \
-		goto out;  \
-	}  \
-} while(0)
+unshin:
 
 
-#ifdef WIN32
-static int bitforce_autodetect_ftdi(void)
-{
-	char devpath[] = "\\\\.\\COMnnnnn";
-	char *devpathnum = &devpath[7];
-	char **bufptrs;
-	char *buf;
-	int found = 0;
-	DWORD i;
-
-	FT_STATUS ftStatus;
-	DWORD numDevs;
-	HMODULE dll = LoadLibrary("FTD2XX.DLL");
-	if (!dll) {
-		applog(LOG_DEBUG, "FTD2XX.DLL failed to load, not using FTDI bitforce autodetect");
-		return 0;
-	}
-	LOAD_SYM(FT_ListDevices);
-	LOAD_SYM(FT_Open);
-	LOAD_SYM(FT_GetComPortNumber);
-	LOAD_SYM(FT_Close);
-	
-	ftStatus = FT_ListDevices(&numDevs, NULL, FT_LIST_NUMBER_ONLY);
-	if (ftStatus != FT_OK) {
-		applog(LOG_DEBUG, "FTDI device count failed, not using FTDI bitforce autodetect");
-		goto out;
-	}
-	applog(LOG_DEBUG, "FTDI reports %u devices", (unsigned)numDevs);
-
-	buf = alloca(65 * numDevs);
-	bufptrs = alloca(sizeof(*bufptrs) * (numDevs + 1));
-
-	for (i = 0; i < numDevs; ++i)
-		bufptrs[i] = &buf[i * 65];
-	bufptrs[numDevs] = NULL;
-	ftStatus = FT_ListDevices(bufptrs, &numDevs, FT_LIST_ALL | FT_OPEN_BY_DESCRIPTION);
-	if (ftStatus != FT_OK) {
-		applog(LOG_DEBUG, "FTDI device list failed, not using FTDI bitforce autodetect");
-		goto out;
-	}
-	
-	for (i = numDevs; i > 0; ) {
-		--i;
-		bufptrs[i][64] = '\0';
-		
-		if (!(strstr(bufptrs[i], "BitFORCE") && strstr(bufptrs[i], "SHA256")))
-			continue;
-		
-		FT_HANDLE ftHandle;
-		if (FT_OK != FT_Open(i, &ftHandle))
-			continue;
-		LONG lComPortNumber;
-		ftStatus = FT_GetComPortNumber(ftHandle, &lComPortNumber);
-		FT_Close(ftHandle);
-		if (FT_OK != ftStatus || lComPortNumber < 0)
-			continue;
-		
-		sprintf(devpathnum, "%d", (int)lComPortNumber);
-		
-		if (bitforce_detect_one(devpath))
-			++found;
-	}
+	usb_uninit(bitforce);
 
 
-out:
-	dlclose(dll);
-	return found;
-}
-#else
-static int bitforce_autodetect_ftdi(void)
-{
-	return 0;
-}
-#endif
+shin:
 
 
-static int bitforce_detect_auto(void)
-{
-	return (serial_autodetect_udev     (bitforce_detect_one, "BitFORCE*SHA256") ?:
-		serial_autodetect_devserial(bitforce_detect_one, "BitFORCE_SHA256") ?:
-		bitforce_autodetect_ftdi() ?:
-		0);
+	free(bitforce->device_path);
+
+	if (bitforce->name != blank)
+		free(bitforce->name);
+
+	if (bitforce->drv->copy)
+		free(bitforce->drv);
+
+	free(bitforce);
+
+	return false;
 }
 }
 
 
 static void bitforce_detect(void)
 static void bitforce_detect(void)
 {
 {
-	serial_detect_auto(&bitforce_api, bitforce_detect_one, bitforce_detect_auto);
+	usb_detect(&bitforce_drv, bitforce_detect_one);
 }
 }
 
 
 static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
 static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
@@ -247,105 +315,17 @@ static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
 static bool bitforce_thread_prepare(struct thr_info *thr)
 static bool bitforce_thread_prepare(struct thr_info *thr)
 {
 {
 	struct cgpu_info *bitforce = thr->cgpu;
 	struct cgpu_info *bitforce = thr->cgpu;
-	int fdDev = BFopen(bitforce->device_path);
 	struct timeval now;
 	struct timeval now;
 
 
-	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, "BFL%i: Opened %s", bitforce->device_id, bitforce->device_path);
 	gettimeofday(&now, NULL);
 	gettimeofday(&now, NULL);
 	get_datestamp(bitforce->init, &now);
 	get_datestamp(bitforce->init, &now);
 
 
 	return true;
 	return true;
 }
 }
 
 
-static void bitforce_clear_buffer(struct cgpu_info *bitforce)
-{
-	int fdDev = bitforce->device_fd;
-	char pdevbuf[0x100];
-	int count = 0;
-
-	if (!fdDev)
-		return;
-
-	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] && (++count < 10));
-	mutex_unlock(&bitforce->device_mutex);
-}
-
-void bitforce_init(struct cgpu_info *bitforce)
-{
-	char *devpath = bitforce->device_path;
-	int fdDev = bitforce->device_fd, retries = 0;
-	char pdevbuf[0x100];
-	char *s;
-
-	applog(LOG_WARNING, "BFL%i: Re-initialising", bitforce->device_id);
-
-	bitforce_clear_buffer(bitforce);
-
-	mutex_lock(&bitforce->device_mutex);
-	if (fdDev) {
-		BFclose(fdDev);
-		sleep(5);
-	}
-	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;
-	}
-
-	do {
-		BFwrite(fdDev, "ZGX", 3);
-		pdevbuf[0] = '\0';
-		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-
-		if (unlikely(!pdevbuf[0])) {
-			mutex_unlock(&bitforce->device_mutex);
-			applog(LOG_ERR, "BFL%i: Error reading/timeout (ZGX)", bitforce->device_id);
-			return;
-		}
-
-		if (retries++)
-			nmsleep(10);
-	} while (!strstr(pdevbuf, "BUSY") && (retries * 10 < BITFORCE_TIMEOUT_MS));
-
-	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;
-	bitforce->sleep_ms = BITFORCE_SLEEP_MS;
-
-	mutex_unlock(&bitforce->device_mutex);
-}
-
 static void bitforce_flash_led(struct cgpu_info *bitforce)
 static void bitforce_flash_led(struct cgpu_info *bitforce)
 {
 {
-	int fdDev = bitforce->device_fd;
-
-	if (!fdDev)
-		return;
+	int err, amount;
 
 
 	/* Do not try to flash the led if we're polling for a result to
 	/* Do not try to flash the led if we're polling for a result to
 	 * minimise the chance of interleaved results */
 	 * minimise the chance of interleaved results */
@@ -353,19 +333,22 @@ static void bitforce_flash_led(struct cgpu_info *bitforce)
 		return;
 		return;
 
 
 	/* It is not critical flashing the led so don't get stuck if we
 	/* It is not critical flashing the led so don't get stuck if we
-	 * can't grab the mutex here */
+	 * can't grab the mutex now */
 	if (mutex_trylock(&bitforce->device_mutex))
 	if (mutex_trylock(&bitforce->device_mutex))
 		return;
 		return;
 
 
-	BFwrite(fdDev, "ZMX", 3);
+	if ((err = usb_write(bitforce, BITFORCE_FLASH, BITFORCE_FLASH_LEN, &amount, C_REQUESTFLASH)) < 0 || amount != BITFORCE_FLASH_LEN) {
+		applog(LOG_ERR, "%s%i: flash request failed (%d:%d)",
+			bitforce->drv->name, bitforce->device_id, amount, err);
+	} else {
+		/* However, this stops anything else getting a reply
+		 * So best to delay any other access to the BFL */
+		nmsleep(4000);
+	}
 
 
 	/* Once we've tried - don't do it until told to again */
 	/* Once we've tried - don't do it until told to again */
 	bitforce->flash_led = false;
 	bitforce->flash_led = false;
 
 
-	/* However, this stops anything else getting a reply
-	 * So best to delay any other access to the BFL */
-	sleep(4);
-
 	mutex_unlock(&bitforce->device_mutex);
 	mutex_unlock(&bitforce->device_mutex);
 
 
 	return; // nothing is returned by the BFL
 	return; // nothing is returned by the BFL
@@ -373,11 +356,12 @@ static void bitforce_flash_led(struct cgpu_info *bitforce)
 
 
 static bool bitforce_get_temp(struct cgpu_info *bitforce)
 static bool bitforce_get_temp(struct cgpu_info *bitforce)
 {
 {
-	int fdDev = bitforce->device_fd;
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	int err, amount;
 	char *s;
 	char *s;
 
 
-	if (!fdDev)
+	// Device is gone
+	if (bitforce->usbinfo.nodev)
 		return false;
 		return false;
 
 
 	/* Do not try to get the temperature if we're polling for a result to
 	/* Do not try to get the temperature if we're polling for a result to
@@ -396,18 +380,30 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 	if (mutex_trylock(&bitforce->device_mutex))
 	if (mutex_trylock(&bitforce->device_mutex))
 		return false;
 		return false;
 
 
-	BFwrite(fdDev, "ZLX", 3);
-	pdevbuf[0] = '\0';
-	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-	mutex_unlock(&bitforce->device_mutex);
-	
-	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "BFL%i: Error: Get temp returned empty string/timed out", bitforce->device_id);
+	if ((err = usb_write(bitforce, BITFORCE_TEMPERATURE, BITFORCE_TEMPERATURE_LEN, &amount, C_REQUESTTEMPERATURE)) < 0 || amount != BITFORCE_TEMPERATURE_LEN) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%i: Error: Request temp invalid/timed out (%d:%d)",
+				bitforce->drv->name, bitforce->device_id, amount, err);
 		bitforce->hw_errors++;
 		bitforce->hw_errors++;
 		return false;
 		return false;
 	}
 	}
 
 
-	if ((!strncasecmp(pdevbuf, "TEMP", 4)) && (s = strchr(pdevbuf + 4, ':'))) {
+	if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETTEMPERATURE)) < 0 || amount < 1) {
+		mutex_unlock(&bitforce->device_mutex);
+		if (err < 0) {
+			applog(LOG_ERR, "%s%i: Error: Get temp return invalid/timed out (%d:%d)",
+					bitforce->drv->name, bitforce->device_id, amount, err);
+		} else {
+			applog(LOG_ERR, "%s%i: Error: Get temp returned nothing (%d:%d)",
+					bitforce->drv->name, bitforce->device_id, amount, err);
+		}
+		bitforce->hw_errors++;
+		return false;
+	}
+
+	mutex_unlock(&bitforce->device_mutex);
+	
+	if ((!strncasecmp(buf, "TEMP", 4)) && (s = strchr(buf + 4, ':'))) {
 		float temp = strtof(s + 1, NULL);
 		float temp = strtof(s + 1, NULL);
 
 
 		/* Cope with older software  that breaks and reads nonsense
 		/* Cope with older software  that breaks and reads nonsense
@@ -418,7 +414,8 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 		if (temp > 0) {
 		if (temp > 0) {
 			bitforce->temp = temp;
 			bitforce->temp = temp;
 			if (unlikely(bitforce->cutofftemp > 0 && temp > bitforce->cutofftemp)) {
 			if (unlikely(bitforce->cutofftemp > 0 && temp > bitforce->cutofftemp)) {
-				applog(LOG_WARNING, "BFL%i: Hit thermal cutoff limit, disabling!", bitforce->device_id);
+				applog(LOG_WARNING, "%s%i: Hit thermal cutoff limit, disabling!",
+							bitforce->drv->name, bitforce->device_id);
 				bitforce->deven = DEV_RECOVER;
 				bitforce->deven = DEV_RECOVER;
 				dev_error(bitforce, REASON_DEV_THERMAL_CUTOFF);
 				dev_error(bitforce, REASON_DEV_THERMAL_CUTOFF);
 			}
 			}
@@ -427,11 +424,12 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 		/* Use the temperature monitor as a kind of watchdog for when
 		/* Use the temperature monitor as a kind of watchdog for when
 		 * our responses are out of sync and flush the buffer to
 		 * our responses are out of sync and flush the buffer to
 		 * hopefully recover */
 		 * hopefully recover */
-		applog(LOG_WARNING, "BFL%i: Garbled response probably throttling, clearing buffer", bitforce->device_id);
+		applog(LOG_WARNING, "%s%i: Garbled response probably throttling, clearing buffer",
+					bitforce->drv->name, bitforce->device_id);
 		dev_error(bitforce, REASON_DEV_THROTTLE);
 		dev_error(bitforce, REASON_DEV_THROTTLE);
 		/* Count throttling episodes as hardware errors */
 		/* Count throttling episodes as hardware errors */
 		bitforce->hw_errors++;
 		bitforce->hw_errors++;
-		bitforce_clear_buffer(bitforce);
+		bitforce_initialise(bitforce, true);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -441,35 +439,53 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 static bool bitforce_send_work(struct thr_info *thr, struct work *work)
 static bool bitforce_send_work(struct thr_info *thr, struct work *work)
 {
 {
 	struct cgpu_info *bitforce = thr->cgpu;
 	struct cgpu_info *bitforce = thr->cgpu;
-	int fdDev = bitforce->device_fd;
 	unsigned char ob[70];
 	unsigned char ob[70];
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	int err, amount;
 	char *s;
 	char *s;
+	char *cmd;
+	int len;
 
 
-	if (!fdDev)
-		return false;
 re_send:
 re_send:
+	if (bitforce->nonce_range) {
+		cmd = BITFORCE_SENDRANGE;
+		len = BITFORCE_SENDRANGE_LEN;
+	} else {
+		cmd = BITFORCE_SENDWORK;
+		len = BITFORCE_SENDWORK_LEN;
+	}
+
 	mutex_lock(&bitforce->device_mutex);
 	mutex_lock(&bitforce->device_mutex);
-	if (bitforce->nonce_range)
-		BFwrite(fdDev, "ZPX", 3);
-	else
-		BFwrite(fdDev, "ZDX", 3);
-	pdevbuf[0] = '\0';
-	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-	if (!pdevbuf[0] || !strncasecmp(pdevbuf, "B", 1)) {
+	if ((err = usb_write(bitforce, cmd, len, &amount, C_REQUESTSENDWORK)) < 0 || amount != len) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%i: request send work failed (%d:%d)",
+				bitforce->drv->name, bitforce->device_id, amount, err);
+		return false;
+	}
+
+	if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_REQUESTSENDWORKSTATUS)) < 0) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%d: read request send work status failed (%d:%d)",
+				bitforce->drv->name, bitforce->device_id, amount, err);
+		return false;
+	}
+
+	if (amount == 0 || !buf[0] || !strncasecmp(buf, "B", 1)) {
 		mutex_unlock(&bitforce->device_mutex);
 		mutex_unlock(&bitforce->device_mutex);
 		nmsleep(WORK_CHECK_INTERVAL_MS);
 		nmsleep(WORK_CHECK_INTERVAL_MS);
 		goto re_send;
 		goto re_send;
-	} else if (unlikely(strncasecmp(pdevbuf, "OK", 2))) {
+	} else if (unlikely(strncasecmp(buf, "OK", 2))) {
 		mutex_unlock(&bitforce->device_mutex);
 		mutex_unlock(&bitforce->device_mutex);
 		if (bitforce->nonce_range) {
 		if (bitforce->nonce_range) {
-			applog(LOG_WARNING, "BFL%i: Does not support nonce range, disabling", bitforce->device_id);
+			applog(LOG_WARNING, "%s%i: Does not support nonce range, disabling",
+						bitforce->drv->name, bitforce->device_id);
 			bitforce->nonce_range = false;
 			bitforce->nonce_range = false;
 			bitforce->sleep_ms *= 5;
 			bitforce->sleep_ms *= 5;
 			bitforce->kname = KNAME_WORK;
 			bitforce->kname = KNAME_WORK;
 			goto re_send;
 			goto re_send;
 		}
 		}
-		applog(LOG_ERR, "BFL%i: Error: Send work reports: %s", bitforce->device_id, pdevbuf);
+		applog(LOG_ERR, "%s%i: Error: Send work reports: %s",
+				bitforce->drv->name, bitforce->device_id, buf);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -479,7 +495,7 @@ re_send:
 	if (!bitforce->nonce_range) {
 	if (!bitforce->nonce_range) {
 		sprintf((char *)ob + 8 + 32 + 12, ">>>>>>>>");
 		sprintf((char *)ob + 8 + 32 + 12, ">>>>>>>>");
 		work->blk.nonce = bitforce->nonces = 0xffffffff;
 		work->blk.nonce = bitforce->nonces = 0xffffffff;
-		BFwrite(fdDev, ob, 60);
+		len = 60;
 	} else {
 	} else {
 		uint32_t *nonce;
 		uint32_t *nonce;
 
 
@@ -491,26 +507,41 @@ re_send:
 		*nonce = htobe32(work->blk.nonce + bitforce->nonces);
 		*nonce = htobe32(work->blk.nonce + bitforce->nonces);
 		work->blk.nonce += bitforce->nonces + 1;
 		work->blk.nonce += bitforce->nonces + 1;
 		sprintf((char *)ob + 8 + 32 + 12 + 8, ">>>>>>>>");
 		sprintf((char *)ob + 8 + 32 + 12 + 8, ">>>>>>>>");
-		BFwrite(fdDev, ob, 68);
+		len = 68;
+	}
+
+	if ((err = usb_write(bitforce, (char *)ob, len, &amount, C_SENDWORK)) < 0 || amount != len) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%i: send work failed (%d:%d)",
+				bitforce->drv->name, bitforce->device_id, amount, err);
+		return false;
+	}
+
+	if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_SENDWORKSTATUS)) < 0) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%d: read send work status failed (%d:%d)",
+				bitforce->drv->name, bitforce->device_id, amount, err);
+		return false;
 	}
 	}
 
 
-	pdevbuf[0] = '\0';
-	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	mutex_unlock(&bitforce->device_mutex);
 	mutex_unlock(&bitforce->device_mutex);
 
 
 	if (opt_debug) {
 	if (opt_debug) {
 		s = bin2hex(ob + 8, 44);
 		s = bin2hex(ob + 8, 44);
-		applog(LOG_DEBUG, "BFL%i: block data: %s", bitforce->device_id, s);
+		applog(LOG_DEBUG, "%s%i: block data: %s",
+				bitforce->drv->name, bitforce->device_id, s);
 		free(s);
 		free(s);
 	}
 	}
 
 
-	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "BFL%i: Error: Send block data returned empty string/timed out", bitforce->device_id);
+	if (amount == 0 || !buf[0]) {
+		applog(LOG_ERR, "%s%i: Error: Send block data returned empty string/timed out",
+				bitforce->drv->name, bitforce->device_id);
 		return false;
 		return false;
 	}
 	}
 
 
-	if (unlikely(strncasecmp(pdevbuf, "OK", 2))) {
-		applog(LOG_ERR, "BFL%i: Error: Send block data reports: %s", bitforce->device_id, pdevbuf);
+	if (unlikely(strncasecmp(buf, "OK", 2))) {
+		applog(LOG_ERR, "%s%i: Error: Send block data reports: %s",
+				bitforce->drv->name, bitforce->device_id, buf);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -521,53 +552,52 @@ re_send:
 static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 {
 {
 	struct cgpu_info *bitforce = thr->cgpu;
 	struct cgpu_info *bitforce = thr->cgpu;
-	int fdDev = bitforce->device_fd;
 	unsigned int delay_time_ms;
 	unsigned int delay_time_ms;
 	struct timeval elapsed;
 	struct timeval elapsed;
 	struct timeval now;
 	struct timeval now;
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	int amount;
 	char *pnoncebuf;
 	char *pnoncebuf;
 	uint32_t nonce;
 	uint32_t nonce;
 
 
-	if (!fdDev)
-		return -1;
-
 	while (1) {
 	while (1) {
 		if (unlikely(thr->work_restart))
 		if (unlikely(thr->work_restart))
 			return 0;
 			return 0;
 
 
 		mutex_lock(&bitforce->device_mutex);
 		mutex_lock(&bitforce->device_mutex);
-		BFwrite(fdDev, "ZFX", 3);
-		pdevbuf[0] = '\0';
-		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
+		usb_write(bitforce, BITFORCE_WORKSTATUS, BITFORCE_WORKSTATUS_LEN, &amount, C_REQUESTWORKSTATUS);
+		usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETWORKSTATUS);
 		mutex_unlock(&bitforce->device_mutex);
 		mutex_unlock(&bitforce->device_mutex);
 
 
 		gettimeofday(&now, NULL);
 		gettimeofday(&now, NULL);
 		timersub(&now, &bitforce->work_start_tv, &elapsed);
 		timersub(&now, &bitforce->work_start_tv, &elapsed);
 
 
 		if (elapsed.tv_sec >= BITFORCE_LONG_TIMEOUT_S) {
 		if (elapsed.tv_sec >= BITFORCE_LONG_TIMEOUT_S) {
-			applog(LOG_ERR, "BFL%i: took %dms - longer than %dms", bitforce->device_id,
+			applog(LOG_ERR, "%s%i: took %dms - longer than %dms",
+				bitforce->drv->name, bitforce->device_id,
 				tv_to_ms(elapsed), BITFORCE_LONG_TIMEOUT_MS);
 				tv_to_ms(elapsed), BITFORCE_LONG_TIMEOUT_MS);
 			return 0;
 			return 0;
 		}
 		}
 
 
-		if (pdevbuf[0] && strncasecmp(pdevbuf, "B", 1)) /* BFL does not respond during throttling */
+		if (amount > 0 && buf[0] && strncasecmp(buf, "B", 1)) /* BFL does not respond during throttling */
 			break;
 			break;
 
 
 		/* if BFL is throttling, no point checking so quickly */
 		/* if BFL is throttling, no point checking so quickly */
-		delay_time_ms = (pdevbuf[0] ? BITFORCE_CHECK_INTERVAL_MS : 2 * WORK_CHECK_INTERVAL_MS);
+		delay_time_ms = (buf[0] ? BITFORCE_CHECK_INTERVAL_MS : 2 * WORK_CHECK_INTERVAL_MS);
 		nmsleep(delay_time_ms);
 		nmsleep(delay_time_ms);
 		bitforce->wait_ms += delay_time_ms;
 		bitforce->wait_ms += delay_time_ms;
 	}
 	}
 
 
 	if (elapsed.tv_sec > BITFORCE_TIMEOUT_S) {
 	if (elapsed.tv_sec > BITFORCE_TIMEOUT_S) {
-		applog(LOG_ERR, "BFL%i: took %dms - longer than %dms", bitforce->device_id,
+		applog(LOG_ERR, "%s%i: took %dms - longer than %dms",
+			bitforce->drv->name, bitforce->device_id,
 			tv_to_ms(elapsed), BITFORCE_TIMEOUT_MS);
 			tv_to_ms(elapsed), BITFORCE_TIMEOUT_MS);
 		dev_error(bitforce, REASON_DEV_OVER_HEAT);
 		dev_error(bitforce, REASON_DEV_OVER_HEAT);
 
 
-		if (!pdevbuf[0])	/* Only return if we got nothing after timeout - there still may be results */
+		/* Only return if we got nothing after timeout - there still may be results */
+		if (amount == 0)
 			return 0;
 			return 0;
-	} else if (!strncasecmp(pdevbuf, "N", 1)) {/* Hashing complete (NONCE-FOUND or NO-NONCE) */
+	} else if (!strncasecmp(buf, BITFORCE_EITHER, BITFORCE_EITHER_LEN)) {
 		/* Simple timing adjustment. Allow a few polls to cope with
 		/* Simple timing adjustment. Allow a few polls to cope with
 		 * OS timer delays being variably reliable. wait_ms will
 		 * OS timer delays being variably reliable. wait_ms will
 		 * always equal sleep_ms when we've waited greater than or
 		 * always equal sleep_ms when we've waited greater than or
@@ -584,26 +614,31 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 		}
 		}
 
 
 		if (delay_time_ms != bitforce->sleep_ms)
 		if (delay_time_ms != bitforce->sleep_ms)
-			  applog(LOG_DEBUG, "BFL%i: Wait time changed to: %d, waited %u", bitforce->device_id, bitforce->sleep_ms, bitforce->wait_ms);
+			  applog(LOG_DEBUG, "%s%i: Wait time changed to: %d, waited %u",
+					bitforce->drv->name, bitforce->device_id,
+					bitforce->sleep_ms, bitforce->wait_ms);
 
 
 		/* Work out the average time taken. Float for calculation, uint for display */
 		/* Work out the average time taken. Float for calculation, uint for display */
 		bitforce->avg_wait_f += (tv_to_ms(elapsed) - bitforce->avg_wait_f) / TIME_AVG_CONSTANT;
 		bitforce->avg_wait_f += (tv_to_ms(elapsed) - bitforce->avg_wait_f) / TIME_AVG_CONSTANT;
 		bitforce->avg_wait_d = (unsigned int) (bitforce->avg_wait_f + 0.5);
 		bitforce->avg_wait_d = (unsigned int) (bitforce->avg_wait_f + 0.5);
 	}
 	}
 
 
-	applog(LOG_DEBUG, "BFL%i: waited %dms until %s", bitforce->device_id, bitforce->wait_ms, pdevbuf);
-	if (!strncasecmp(&pdevbuf[2], "-", 1))
+	applog(LOG_DEBUG, "%s%i: waited %dms until %s",
+			bitforce->drv->name, bitforce->device_id,
+			bitforce->wait_ms, buf);
+	if (!strncasecmp(buf, BITFORCE_NO_NONCE, BITFORCE_NO_NONCE_MATCH))
 		return bitforce->nonces;   /* No valid nonce found */
 		return bitforce->nonces;   /* No valid nonce found */
-	else if (!strncasecmp(pdevbuf, "I", 1))
+	else if (!strncasecmp(buf, BITFORCE_IDLE, BITFORCE_IDLE_MATCH))
 		return 0;	/* Device idle */
 		return 0;	/* Device idle */
-	else if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) {
+	else if (strncasecmp(buf, BITFORCE_NONCE, BITFORCE_NONCE_LEN)) {
 		bitforce->hw_errors++;
 		bitforce->hw_errors++;
-		applog(LOG_WARNING, "BFL%i: Error: Get result reports: %s", bitforce->device_id, pdevbuf);
-		bitforce_clear_buffer(bitforce);
+		applog(LOG_WARNING, "%s%i: Error: Get result reports: %s",
+			bitforce->drv->name, bitforce->device_id, buf);
+		bitforce_initialise(bitforce, true);
 		return 0;
 		return 0;
 	}
 	}
 
 
-	pnoncebuf = &pdevbuf[12];
+	pnoncebuf = &buf[12];
 
 
 	while (1) {
 	while (1) {
 		hex2bin((void*)&nonce, pnoncebuf, 4);
 		hex2bin((void*)&nonce, pnoncebuf, 4);
@@ -612,7 +647,8 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 #endif
 #endif
 		if (unlikely(bitforce->nonce_range && (nonce >= work->blk.nonce ||
 		if (unlikely(bitforce->nonce_range && (nonce >= work->blk.nonce ||
 			(work->blk.nonce > 0 && nonce < work->blk.nonce - bitforce->nonces - 1)))) {
 			(work->blk.nonce > 0 && nonce < work->blk.nonce - bitforce->nonces - 1)))) {
-				applog(LOG_WARNING, "BFL%i: Disabling broken nonce range support", bitforce->device_id);
+				applog(LOG_WARNING, "%s%i: Disabling broken nonce range support",
+					bitforce->drv->name, bitforce->device_id);
 				bitforce->nonce_range = false;
 				bitforce->nonce_range = false;
 				work->blk.nonce = 0xffffffff;
 				work->blk.nonce = 0xffffffff;
 				bitforce->sleep_ms *= 5;
 				bitforce->sleep_ms *= 5;
@@ -628,19 +664,16 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 	return bitforce->nonces;
 	return bitforce->nonces;
 }
 }
 
 
-static void bitforce_shutdown(struct thr_info *thr)
+static void bitforce_shutdown(__maybe_unused struct thr_info *thr)
 {
 {
-	struct cgpu_info *bitforce = thr->cgpu;
-
-	BFclose(bitforce->device_fd);
-	bitforce->device_fd = 0;
+//	struct cgpu_info *bitforce = thr->cgpu;
 }
 }
 
 
 static void biforce_thread_enable(struct thr_info *thr)
 static void biforce_thread_enable(struct thr_info *thr)
 {
 {
 	struct cgpu_info *bitforce = thr->cgpu;
 	struct cgpu_info *bitforce = thr->cgpu;
 
 
-	bitforce_init(bitforce);
+	bitforce_initialise(bitforce, true);
 }
 }
 
 
 static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce)
 static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce)
@@ -649,6 +682,10 @@ static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_
 	bool send_ret;
 	bool send_ret;
 	int64_t ret;
 	int64_t ret;
 
 
+	// Device is gone
+	if (bitforce->usbinfo.nodev)
+		return -1;
+
 	send_ret = bitforce_send_work(thr, work);
 	send_ret = bitforce_send_work(thr, work);
 
 
 	if (!restart_wait(bitforce->sleep_ms))
 	if (!restart_wait(bitforce->sleep_ms))
@@ -665,11 +702,11 @@ static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_
 
 
 	if (ret == -1) {
 	if (ret == -1) {
 		ret = 0;
 		ret = 0;
-		applog(LOG_ERR, "BFL%i: Comms error", bitforce->device_id);
+		applog(LOG_ERR, "%s%i: Comms error", bitforce->drv->name, bitforce->device_id);
 		dev_error(bitforce, REASON_DEV_COMMS_ERROR);
 		dev_error(bitforce, REASON_DEV_COMMS_ERROR);
 		bitforce->hw_errors++;
 		bitforce->hw_errors++;
 		/* empty read buffer */
 		/* empty read buffer */
-		bitforce_clear_buffer(bitforce);
+		bitforce_initialise(bitforce, true);
 	}
 	}
 	return ret;
 	return ret;
 }
 }
@@ -692,7 +729,8 @@ static bool bitforce_thread_init(struct thr_info *thr)
 	/* Pause each new thread at least 100ms between initialising
 	/* Pause each new thread at least 100ms between initialising
 	 * so the devices aren't making calls all at the same time. */
 	 * so the devices aren't making calls all at the same time. */
 	wait = thr->id * MAX_START_DELAY_MS;
 	wait = thr->id * MAX_START_DELAY_MS;
-	applog(LOG_DEBUG, "BFL%i: Delaying start by %dms", bitforce->device_id, wait / 1000);
+	applog(LOG_DEBUG, "%s%d: Delaying start by %dms",
+			bitforce->drv->name, bitforce->device_id, wait / 1000);
 	nmsleep(wait);
 	nmsleep(wait);
 
 
 	return true;
 	return true;
@@ -712,12 +750,12 @@ static struct api_data *bitforce_api_stats(struct cgpu_info *cgpu)
 	return root;
 	return root;
 }
 }
 
 
-struct device_api bitforce_api = {
-	.dname = "bitforce",
+struct device_drv bitforce_drv = {
+	.drv_id = DRIVER_BITFORCE,
+	.dname = "BitForce",
 	.name = "BFL",
 	.name = "BFL",
-	.api_detect = bitforce_detect,
+	.drv_detect = bitforce_detect,
 	.get_api_stats = bitforce_api_stats,
 	.get_api_stats = bitforce_api_stats,
-	.reinit_device = bitforce_init,
 	.get_statline_before = get_bitforce_statline_before,
 	.get_statline_before = get_bitforce_statline_before,
 	.get_stats = bitforce_get_stats,
 	.get_stats = bitforce_get_stats,
 	.identify_device = bitforce_identify,
 	.identify_device = bitforce_identify,

+ 5 - 4
driver-cpu.c

@@ -758,7 +758,7 @@ static void cpu_detect()
 		struct cgpu_info *cgpu;
 		struct cgpu_info *cgpu;
 
 
 		cgpu = &cpus[i];
 		cgpu = &cpus[i];
-		cgpu->api = &cpu_api;
+		cgpu->drv = &cpu_drv;
 		cgpu->deven = DEV_ENABLED;
 		cgpu->deven = DEV_ENABLED;
 		cgpu->threads = 1;
 		cgpu->threads = 1;
 		cgpu->kname = algo_names[opt_algo];
 		cgpu->kname = algo_names[opt_algo];
@@ -768,7 +768,7 @@ static void cpu_detect()
 
 
 static void reinit_cpu_device(struct cgpu_info *cpu)
 static void reinit_cpu_device(struct cgpu_info *cpu)
 {
 {
-	tq_push(thr_info[cpur_thr_id].q, cpu);
+	tq_push(control_thr[cpur_thr_id].q, cpu);
 }
 }
 
 
 static bool cpu_thread_prepare(struct thr_info *thr)
 static bool cpu_thread_prepare(struct thr_info *thr)
@@ -843,10 +843,11 @@ CPUSearch:
 	return last_nonce - first_nonce + 1;
 	return last_nonce - first_nonce + 1;
 }
 }
 
 
-struct device_api cpu_api = {
+struct device_drv cpu_drv = {
+	.drv_id = DRIVER_CPU,
 	.dname = "cpu",
 	.dname = "cpu",
 	.name = "CPU",
 	.name = "CPU",
-	.api_detect = cpu_detect,
+	.drv_detect = cpu_detect,
 	.reinit_device = reinit_cpu_device,
 	.reinit_device = reinit_cpu_device,
 	.thread_prepare = cpu_thread_prepare,
 	.thread_prepare = cpu_thread_prepare,
 	.can_limit_work = cpu_can_limit_work,
 	.can_limit_work = cpu_can_limit_work,

+ 1 - 1
driver-cpu.h

@@ -53,7 +53,7 @@ enum sha256_algos {
 
 
 extern const char *algo_names[];
 extern const char *algo_names[];
 extern bool opt_usecpu;
 extern bool opt_usecpu;
-extern struct device_api cpu_api;
+extern struct device_drv cpu_drv;
 
 
 extern char *set_algo(const char *arg, enum sha256_algos *algo);
 extern char *set_algo(const char *arg, enum sha256_algos *algo);
 extern void show_algo(char buf[OPT_SHOW_LEN], const enum sha256_algos *algo);
 extern void show_algo(char buf[OPT_SHOW_LEN], const enum sha256_algos *algo);

+ 10 - 9
driver-icarus.c

@@ -208,7 +208,7 @@ static struct ICARUS_INFO **icarus_info;
 //
 //
 static int option_offset = -1;
 static int option_offset = -1;
 
 
-struct device_api icarus_api;
+struct device_drv icarus_drv;
 
 
 static void rev(unsigned char *s, size_t l)
 static void rev(unsigned char *s, size_t l)
 {
 {
@@ -571,7 +571,7 @@ static bool icarus_detect_one(const char *devpath)
 	/* We have a real Icarus! */
 	/* We have a real Icarus! */
 	struct cgpu_info *icarus;
 	struct cgpu_info *icarus;
 	icarus = calloc(1, sizeof(struct cgpu_info));
 	icarus = calloc(1, sizeof(struct cgpu_info));
-	icarus->api = &icarus_api;
+	icarus->drv = &icarus_drv;
 	icarus->device_path = strdup(devpath);
 	icarus->device_path = strdup(devpath);
 	icarus->device_fd = -1;
 	icarus->device_fd = -1;
 	icarus->threads = 1;
 	icarus->threads = 1;
@@ -609,7 +609,7 @@ static bool icarus_detect_one(const char *devpath)
 
 
 static void icarus_detect()
 static void icarus_detect()
 {
 {
-	serial_detect(&icarus_api, icarus_detect_one);
+	serial_detect(&icarus_drv, icarus_detect_one);
 }
 }
 
 
 static bool icarus_prepare(struct thr_info *thr)
 static bool icarus_prepare(struct thr_info *thr)
@@ -668,7 +668,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	icarus = thr->cgpu;
 	icarus = thr->cgpu;
 	if (icarus->device_fd == -1)
 	if (icarus->device_fd == -1)
 		if (!icarus_prepare(thr)) {
 		if (!icarus_prepare(thr)) {
-			applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id);
+			applog(LOG_ERR, "%s%i: Comms error", icarus->drv->name, icarus->device_id);
 			dev_error(icarus, REASON_DEV_COMMS_ERROR);
 			dev_error(icarus, REASON_DEV_COMMS_ERROR);
 
 
 			// fail the device if the reopen attempt fails
 			// fail the device if the reopen attempt fails
@@ -688,7 +688,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	ret = icarus_write(fd, ob_bin, sizeof(ob_bin));
 	ret = icarus_write(fd, ob_bin, sizeof(ob_bin));
 	if (ret) {
 	if (ret) {
 		do_icarus_close(thr);
 		do_icarus_close(thr);
-		applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id);
+		applog(LOG_ERR, "%s%i: Comms error", icarus->drv->name, icarus->device_id);
 		dev_error(icarus, REASON_DEV_COMMS_ERROR);
 		dev_error(icarus, REASON_DEV_COMMS_ERROR);
 		return 0;	/* This should never happen */
 		return 0;	/* This should never happen */
 	}
 	}
@@ -708,7 +708,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	ret = icarus_gets(nonce_bin, fd, &tv_finish, thr, info->read_count);
 	ret = icarus_gets(nonce_bin, fd, &tv_finish, thr, info->read_count);
 	if (ret == ICA_GETS_ERROR) {
 	if (ret == ICA_GETS_ERROR) {
 		do_icarus_close(thr);
 		do_icarus_close(thr);
-		applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id);
+		applog(LOG_ERR, "%s%i: Comms error", icarus->drv->name, icarus->device_id);
 		dev_error(icarus, REASON_DEV_COMMS_ERROR);
 		dev_error(icarus, REASON_DEV_COMMS_ERROR);
 		return 0;
 		return 0;
 	}
 	}
@@ -900,10 +900,11 @@ static void icarus_shutdown(struct thr_info *thr)
 	do_icarus_close(thr);
 	do_icarus_close(thr);
 }
 }
 
 
-struct device_api icarus_api = {
-	.dname = "icarus",
+struct device_drv icarus_drv = {
+	.drv_id = DRIVER_ICARUS,
+	.dname = "Icarus",
 	.name = "ICA",
 	.name = "ICA",
-	.api_detect = icarus_detect,
+	.drv_detect = icarus_detect,
 	.get_api_stats = icarus_api_stats,
 	.get_api_stats = icarus_api_stats,
 	.thread_prepare = icarus_prepare,
 	.thread_prepare = icarus_prepare,
 	.scanhash = icarus_scanhash,
 	.scanhash = icarus_scanhash,

+ 138 - 122
driver-modminer.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2012 Andrew Smith
+ * Copyright 2012-2013 Andrew Smith
  * Copyright 2012 Luke Dashjr
  * Copyright 2012 Luke Dashjr
  *
  *
  * This program is free software; you can redistribute it and/or modify it
  * This program is free software; you can redistribute it and/or modify it
@@ -87,7 +87,7 @@
 // Limit when reducing shares_to_good
 // Limit when reducing shares_to_good
 #define MODMINER_MIN_BACK 12
 #define MODMINER_MIN_BACK 12
 
 
-struct device_api modminer_api;
+struct device_drv modminer_drv;
 
 
 // 45 noops sent when detecting, in case the device was left in "start job" reading
 // 45 noops sent when detecting, in case the device was left in "start job" reading
 static const char NOOP[] = MODMINER_PING "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
 static const char NOOP[] = MODMINER_PING "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
@@ -100,15 +100,15 @@ static void do_ping(struct cgpu_info *modminer)
 	// Don't care if it fails
 	// Don't care if it fails
 	err = usb_write(modminer, (char *)NOOP, sizeof(NOOP)-1, &amount, C_PING);
 	err = usb_write(modminer, (char *)NOOP, sizeof(NOOP)-1, &amount, C_PING);
 	applog(LOG_DEBUG, "%s%u: flush noop got %d err %d",
 	applog(LOG_DEBUG, "%s%u: flush noop got %d err %d",
-		modminer->api->name, modminer->fpgaid, amount, err);
+		modminer->drv->name, modminer->fpgaid, amount, err);
 
 
 	// Clear any outstanding data
 	// Clear any outstanding data
 	while ((err = usb_read(modminer, buf, sizeof(buf)-1, &amount, C_CLEAR)) == 0 && amount > 0)
 	while ((err = usb_read(modminer, buf, sizeof(buf)-1, &amount, C_CLEAR)) == 0 && amount > 0)
 		applog(LOG_DEBUG, "%s%u: clear got %d",
 		applog(LOG_DEBUG, "%s%u: clear got %d",
-			modminer->api->name, modminer->fpgaid, amount);
+			modminer->drv->name, modminer->fpgaid, amount);
 
 
 	applog(LOG_DEBUG, "%s%u: final clear got %d err %d",
 	applog(LOG_DEBUG, "%s%u: final clear got %d err %d",
-		modminer->api->name, modminer->fpgaid, amount, err);
+		modminer->drv->name, modminer->fpgaid, amount, err);
 }
 }
 
 
 static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devices *found)
 static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devices *found)
@@ -121,42 +121,57 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 
 
 	struct cgpu_info *modminer = NULL;
 	struct cgpu_info *modminer = NULL;
 	modminer = calloc(1, sizeof(*modminer));
 	modminer = calloc(1, sizeof(*modminer));
-	modminer->api = &modminer_api;
+	modminer->drv = &modminer_drv;
 	modminer->modminer_mutex = calloc(1, sizeof(*(modminer->modminer_mutex)));
 	modminer->modminer_mutex = calloc(1, sizeof(*(modminer->modminer_mutex)));
 	mutex_init(modminer->modminer_mutex);
 	mutex_init(modminer->modminer_mutex);
 	modminer->fpgaid = (char)0;
 	modminer->fpgaid = (char)0;
 
 
-	if (!usb_init(modminer, dev, found))
+	if (!usb_init(modminer, dev, found)) {
+		applog(LOG_ERR, "%s detect (%d:%d) failed to initialise (incorrect device?)",
+			modminer->drv->dname,
+			(int)(modminer->usbinfo.bus_number),
+			(int)(modminer->usbinfo.device_address));
 		goto shin;
 		goto shin;
+	}
+
+	sprintf(devpath, "%d:%d",
+			(int)(modminer->usbinfo.bus_number),
+			(int)(modminer->usbinfo.device_address));
 
 
 	do_ping(modminer);
 	do_ping(modminer);
 
 
 	if ((err = usb_write(modminer, MODMINER_GET_VERSION, 1, &amount, C_REQUESTVERSION)) < 0 || amount != 1) {
 	if ((err = usb_write(modminer, MODMINER_GET_VERSION, 1, &amount, C_REQUESTVERSION)) < 0 || amount != 1) {
-		applog(LOG_ERR, "ModMiner detect: send version request failed (%d:%d)", amount, err);
+		applog(LOG_ERR, "%s detect (%s) send version request failed (%d:%d)",
+			modminer->drv->dname, devpath, amount, err);
 		goto unshin;
 		goto unshin;
 	}
 	}
 
 
 	if ((err = usb_read(modminer, buf, sizeof(buf)-1, &amount, C_GETVERSION)) < 0 || amount < 1) {
 	if ((err = usb_read(modminer, buf, sizeof(buf)-1, &amount, C_GETVERSION)) < 0 || amount < 1) {
 		if (err < 0)
 		if (err < 0)
-			applog(LOG_ERR, "ModMiner detect: no version reply (%d)", err);
+			applog(LOG_ERR, "%s detect (%s) no version reply (%d)",
+				modminer->drv->dname, devpath, err);
 		else
 		else
-			applog(LOG_ERR, "ModMiner detect: empty version reply (%d)", amount);
+			applog(LOG_ERR, "%s detect (%s) empty version reply (%d)",
+				modminer->drv->dname, devpath, amount);
 
 
-		applog(LOG_DEBUG, "ModMiner detect: check the firmware");
+		applog(LOG_DEBUG, "%s detect (%s) check the firmware",
+				modminer->drv->dname, devpath);
 
 
 		goto unshin;
 		goto unshin;
 	}
 	}
 	buf[amount] = '\0';
 	buf[amount] = '\0';
 	devname = strdup(buf);
 	devname = strdup(buf);
-	applog(LOG_DEBUG, "ModMiner identified as: %s", devname);
+	applog(LOG_DEBUG, "%s (%s) identified as: %s", modminer->drv->dname, devpath, devname);
 
 
 	if ((err = usb_write(modminer, MODMINER_FPGA_COUNT, 1, &amount, C_REQUESTFPGACOUNT) < 0 || amount != 1)) {
 	if ((err = usb_write(modminer, MODMINER_FPGA_COUNT, 1, &amount, C_REQUESTFPGACOUNT) < 0 || amount != 1)) {
-		applog(LOG_ERR, "ModMiner detect: FPGA count request failed (%d:%d)", amount, err);
+		applog(LOG_ERR, "%s detect (%s) FPGA count request failed (%d:%d)",
+			modminer->drv->dname, devpath, amount, err);
 		goto unshin;
 		goto unshin;
 	}
 	}
 
 
 	if ((err = usb_read(modminer, buf, 1, &amount, C_GETFPGACOUNT)) < 0 || amount != 1) {
 	if ((err = usb_read(modminer, buf, 1, &amount, C_GETFPGACOUNT)) < 0 || amount != 1) {
-		applog(LOG_ERR, "ModMiner detect: no FPGA count reply (%d:%d)", amount, err);
+		applog(LOG_ERR, "%s detect (%s) no FPGA count reply (%d:%d)",
+			modminer->drv->dname, devpath, amount, err);
 		goto unshin;
 		goto unshin;
 	}
 	}
 
 
@@ -164,16 +179,19 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 	// can detect with modminer->cgusb->serial ?
 	// can detect with modminer->cgusb->serial ?
 
 
 	if (buf[0] == 0) {
 	if (buf[0] == 0) {
-		applog(LOG_ERR, "ModMiner detect: zero FPGA count from %s", devname);
+		applog(LOG_ERR, "%s detect (%s) zero FPGA count from %s",
+			modminer->drv->dname, devpath, devname);
 		goto unshin;
 		goto unshin;
 	}
 	}
 
 
 	if (buf[0] < 1 || buf[0] > 4) {
 	if (buf[0] < 1 || buf[0] > 4) {
-		applog(LOG_ERR, "ModMiner detect: invalid FPGA count (%u) from %s", buf[0], devname);
+		applog(LOG_ERR, "%s detect (%s) invalid FPGA count (%u) from %s",
+			modminer->drv->dname, devpath, buf[0], devname);
 		goto unshin;
 		goto unshin;
 	}
 	}
 
 
-	applog(LOG_DEBUG, "ModMiner %s has %u FPGAs", devname, buf[0]);
+	applog(LOG_DEBUG, "%s (%s) %s has %u FPGAs",
+		modminer->drv->dname, devpath, devname, buf[0]);
 
 
 	modminer->name = devname;
 	modminer->name = devname;
 
 
@@ -182,19 +200,21 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 	for (i = 0; i < buf[0]; i++) {
 	for (i = 0; i < buf[0]; i++) {
 		struct cgpu_info *tmp = calloc(1, sizeof(*tmp));
 		struct cgpu_info *tmp = calloc(1, sizeof(*tmp));
 
 
-		tmp->api = modminer->api;
+		tmp->drv = copy_drv(modminer->drv);
 		tmp->name = devname;
 		tmp->name = devname;
 
 
 		sprintf(devpath, "%d:%d:%d",
 		sprintf(devpath, "%d:%d:%d",
-			(int)(modminer->usbdev->bus_number),
-			(int)(modminer->usbdev->device_address),
+			(int)(modminer->usbinfo.bus_number),
+			(int)(modminer->usbinfo.device_address),
 			i);
 			i);
 
 
 		tmp->device_path = strdup(devpath);
 		tmp->device_path = strdup(devpath);
 		tmp->usbdev = modminer->usbdev;
 		tmp->usbdev = modminer->usbdev;
+		tmp->usbinfo.bus_number = modminer->usbinfo.bus_number;
+		tmp->usbinfo.device_address = modminer->usbinfo.device_address;
 		// Only the first copy gets the already used stats
 		// Only the first copy gets the already used stats
 		if (!added)
 		if (!added)
-			tmp->usbstat = modminer->usbstat;
+			tmp->usbinfo.usbstat = modminer->usbinfo.usbstat;
 		tmp->fpgaid = (char)i;
 		tmp->fpgaid = (char)i;
 		tmp->modminer_mutex = modminer->modminer_mutex;
 		tmp->modminer_mutex = modminer->modminer_mutex;
 		tmp->deven = DEV_ENABLED;
 		tmp->deven = DEV_ENABLED;
@@ -202,6 +222,8 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 
 
 		if (!add_cgpu(tmp)) {
 		if (!add_cgpu(tmp)) {
 			free(tmp->device_path);
 			free(tmp->device_path);
+			if (tmp->drv->copy)
+				free(tmp->drv);
 			free(tmp);
 			free(tmp);
 			goto unshin;
 			goto unshin;
 		}
 		}
@@ -211,6 +233,9 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 		added = true;
 		added = true;
 	}
 	}
 
 
+	if (modminer->drv->copy)
+		free(modminer->drv);
+
 	free(modminer);
 	free(modminer);
 
 
 	return true;
 	return true;
@@ -223,6 +248,9 @@ shin:
 	if (!added)
 	if (!added)
 		free(modminer->modminer_mutex);
 		free(modminer->modminer_mutex);
 
 
+	if (modminer->drv->copy)
+		free(modminer->drv);
+
 	free(modminer);
 	free(modminer);
 
 
 	if (added)
 	if (added)
@@ -233,7 +261,7 @@ shin:
 
 
 static void modminer_detect()
 static void modminer_detect()
 {
 {
-	usb_detect(&modminer_api, modminer_detect_one);
+	usb_detect(&modminer_drv, modminer_detect_one);
 }
 }
 
 
 static bool get_expect(struct cgpu_info *modminer, FILE *f, char c)
 static bool get_expect(struct cgpu_info *modminer, FILE *f, char c)
@@ -242,13 +270,13 @@ static bool get_expect(struct cgpu_info *modminer, FILE *f, char c)
 
 
 	if (fread(&buf, 1, 1, f) != 1) {
 	if (fread(&buf, 1, 1, f) != 1) {
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream (%c)",
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream (%c)",
-				modminer->api->name, modminer->device_id, errno, c);
+				modminer->drv->name, modminer->device_id, errno, c);
 		return false;
 		return false;
 	}
 	}
 
 
 	if (buf != c) {
 	if (buf != c) {
-		applog(LOG_ERR, "%s%u: firmware code mismatch (%c)",
-				modminer->api->name, modminer->device_id, c);
+		applog(LOG_ERR, "%s%u: bitstream code mismatch (%c)",
+				modminer->drv->name, modminer->device_id, c);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -262,7 +290,7 @@ static bool get_info(struct cgpu_info *modminer, FILE *f, char *buf, int bufsiz,
 
 
 	if (fread(siz, 2, 1, f) != 1) {
 	if (fread(siz, 2, 1, f) != 1) {
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream '%s' len",
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream '%s' len",
-			modminer->api->name, modminer->device_id, errno, name);
+			modminer->drv->name, modminer->device_id, errno, name);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -270,13 +298,13 @@ static bool get_info(struct cgpu_info *modminer, FILE *f, char *buf, int bufsiz,
 
 
 	if (len >= bufsiz) {
 	if (len >= bufsiz) {
 		applog(LOG_ERR, "%s%u: Bitstream '%s' len too large (%d)",
 		applog(LOG_ERR, "%s%u: Bitstream '%s' len too large (%d)",
-			modminer->api->name, modminer->device_id, name, len);
+			modminer->drv->name, modminer->device_id, name, len);
 		return false;
 		return false;
 	}
 	}
 
 
 	if (fread(buf, len, 1, f) != 1) {
 	if (fread(buf, len, 1, f) != 1) {
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream '%s'", errno,
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream '%s'", errno,
-			modminer->api->name, modminer->device_id, errno, name);
+			modminer->drv->name, modminer->device_id, errno, name);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -302,7 +330,7 @@ static bool get_status_timeout(struct cgpu_info *modminer, char *msg, unsigned i
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error (%d:%d) getting %s reply",
 		applog(LOG_ERR, "%s%u: Error (%d:%d) getting %s reply",
-			modminer->api->name, modminer->device_id, amount, err, msg);
+			modminer->drv->name, modminer->device_id, amount, err, msg);
 
 
 		return false;
 		return false;
 	}
 	}
@@ -311,7 +339,7 @@ static bool get_status_timeout(struct cgpu_info *modminer, char *msg, unsigned i
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error, invalid %s reply (was %d should be 1)",
 		applog(LOG_ERR, "%s%u: Error, invalid %s reply (was %d should be 1)",
-			modminer->api->name, modminer->device_id, msg, buf[0]);
+			modminer->drv->name, modminer->device_id, msg, buf[0]);
 
 
 		return false;
 		return false;
 	}
 	}
@@ -343,7 +371,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error (%d) opening bitstream file %s",
 		applog(LOG_ERR, "%s%u: Error (%d) opening bitstream file %s",
-			modminer->api->name, modminer->device_id, errno, bsfile);
+			modminer->drv->name, modminer->device_id, errno, bsfile);
 
 
 		return false;
 		return false;
 	}
 	}
@@ -352,7 +380,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream magic",
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream magic",
-			modminer->api->name, modminer->device_id, errno);
+			modminer->drv->name, modminer->device_id, errno);
 
 
 		goto dame;
 		goto dame;
 	}
 	}
@@ -361,7 +389,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: bitstream has incorrect magic (%u,%u) instead of (%u,%u)",
 		applog(LOG_ERR, "%s%u: bitstream has incorrect magic (%u,%u) instead of (%u,%u)",
-			modminer->api->name, modminer->device_id,
+			modminer->drv->name, modminer->device_id,
 			buf[0], buf[1],
 			buf[0], buf[1],
 			BITSTREAM_MAGIC_0, BITSTREAM_MAGIC_1);
 			BITSTREAM_MAGIC_0, BITSTREAM_MAGIC_1);
 
 
@@ -372,7 +400,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error (%d) bitstream seek failed",
 		applog(LOG_ERR, "%s%u: Error (%d) bitstream seek failed",
-			modminer->api->name, modminer->device_id, errno);
+			modminer->drv->name, modminer->device_id, errno);
 
 
 		goto dame;
 		goto dame;
 	}
 	}
@@ -384,7 +412,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		goto undame;
 		goto undame;
 
 
 	applog(LOG_DEBUG, "%s%u: bitstream file '%s' info:",
 	applog(LOG_DEBUG, "%s%u: bitstream file '%s' info:",
-		modminer->api->name, modminer->device_id, bsfile);
+		modminer->drv->name, modminer->device_id, bsfile);
 
 
 	applog(LOG_DEBUG, " Design name: '%s'", buf);
 	applog(LOG_DEBUG, " Design name: '%s'", buf);
 
 
@@ -399,7 +427,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Bad usercode in bitstream file",
 		applog(LOG_ERR, "%s%u: Bad usercode in bitstream file",
-			modminer->api->name, modminer->device_id);
+			modminer->drv->name, modminer->device_id);
 
 
 		goto dame;
 		goto dame;
 	}
 	}
@@ -408,7 +436,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: bitstream doesn't support user code",
 		applog(LOG_ERR, "%s%u: bitstream doesn't support user code",
-			modminer->api->name, modminer->device_id);
+			modminer->drv->name, modminer->device_id);
 
 
 		goto dame;
 		goto dame;
 	}
 	}
@@ -446,7 +474,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream data len",
 		applog(LOG_ERR, "%s%u: Error (%d) reading bitstream data len",
-			modminer->api->name, modminer->device_id, errno);
+			modminer->drv->name, modminer->device_id, errno);
 
 
 		goto dame;
 		goto dame;
 	}
 	}
@@ -460,7 +488,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		*ptr = '\0';
 		*ptr = '\0';
 
 
 	applog(LOG_WARNING, "%s%u: Programming all FPGA on %s ... Mining will not start until complete",
 	applog(LOG_WARNING, "%s%u: Programming all FPGA on %s ... Mining will not start until complete",
-		modminer->api->name, modminer->device_id, devmsg);
+		modminer->drv->name, modminer->device_id, devmsg);
 
 
 	buf[0] = MODMINER_PROGRAM;
 	buf[0] = MODMINER_PROGRAM;
 	buf[1] = fpgaid;
 	buf[1] = fpgaid;
@@ -473,7 +501,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Program init failed (%d:%d)",
 		applog(LOG_ERR, "%s%u: Program init failed (%d:%d)",
-			modminer->api->name, modminer->device_id, amount, err);
+			modminer->drv->name, modminer->device_id, amount, err);
 
 
 		goto dame;
 		goto dame;
 	}
 	}
@@ -492,7 +520,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 			mutex_unlock(modminer->modminer_mutex);
 			mutex_unlock(modminer->modminer_mutex);
 
 
 			applog(LOG_ERR, "%s%u: bitstream file read error %d (%d bytes left)",
 			applog(LOG_ERR, "%s%u: bitstream file read error %d (%d bytes left)",
-				modminer->api->name, modminer->device_id, errno, len);
+				modminer->drv->name, modminer->device_id, errno, len);
 
 
 			goto dame;
 			goto dame;
 		}
 		}
@@ -507,7 +535,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 
 
 				if (opt_debug)
 				if (opt_debug)
 					applog(LOG_DEBUG, "%s%u: Program timeout (%d:%d) sent %d tries %d",
 					applog(LOG_DEBUG, "%s%u: Program timeout (%d:%d) sent %d tries %d",
-						modminer->api->name, modminer->device_id,
+						modminer->drv->name, modminer->device_id,
 						amount, err, remaining, tries);
 						amount, err, remaining, tries);
 
 
 				if (!get_status(modminer, "write status", C_PROGRAMSTATUS2))
 				if (!get_status(modminer, "write status", C_PROGRAMSTATUS2))
@@ -517,7 +545,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 				mutex_unlock(modminer->modminer_mutex);
 				mutex_unlock(modminer->modminer_mutex);
 
 
 				applog(LOG_ERR, "%s%u: Program failed (%d:%d) sent %d",
 				applog(LOG_ERR, "%s%u: Program failed (%d:%d) sent %d",
-					modminer->api->name, modminer->device_id, amount, err, remaining);
+					modminer->drv->name, modminer->device_id, amount, err, remaining);
 
 
 				goto dame;
 				goto dame;
 			}
 			}
@@ -532,7 +560,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		if (upto >= nextmsg) {
 		if (upto >= nextmsg) {
 			applog(LOG_WARNING,
 			applog(LOG_WARNING,
 				"%s%u: Programming %.1f%% (%d out of %d)",
 				"%s%u: Programming %.1f%% (%d out of %d)",
-				modminer->api->name, modminer->device_id, upto*100, (totlen - len), totlen);
+				modminer->drv->name, modminer->device_id, upto*100, (totlen - len), totlen);
 
 
 			nextmsg += 0.1;
 			nextmsg += 0.1;
 		}
 		}
@@ -542,7 +570,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		goto undame;
 		goto undame;
 
 
 	applog(LOG_WARNING, "%s%u: Programming completed for all FPGA on %s",
 	applog(LOG_WARNING, "%s%u: Programming completed for all FPGA on %s",
-		modminer->api->name, modminer->device_id, devmsg);
+		modminer->drv->name, modminer->device_id, devmsg);
 
 
 	// Give it a 2/3s delay after programming
 	// Give it a 2/3s delay after programming
 	nmsleep(666);
 	nmsleep(666);
@@ -567,8 +595,6 @@ static bool modminer_fpga_prepare(struct thr_info *thr)
 
 
 	struct modminer_fpga_state *state;
 	struct modminer_fpga_state *state;
 	state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state));
 	state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state));
-	state->next_work_cmd[0] = MODMINER_SEND_WORK;
-	state->next_work_cmd[1] = modminer->fpgaid;
 	state->shares_to_good = MODMINER_EARLY_UP;
 	state->shares_to_good = MODMINER_EARLY_UP;
 	state->overheated = false;
 	state->overheated = false;
 
 
@@ -599,6 +625,7 @@ static bool modminer_fpga_prepare(struct thr_info *thr)
  *
  *
  * N.B. clock must always be a multiple of 2
  * N.B. clock must always be a multiple of 2
  */
  */
+static const char *clocknodev = "clock failed - no device";
 static const char *clockoldwork = "clock already changed for this work";
 static const char *clockoldwork = "clock already changed for this work";
 static const char *clocktoolow = "clock too low";
 static const char *clocktoolow = "clock too low";
 static const char *clocktoohi = "clock too high";
 static const char *clocktoohi = "clock too high";
@@ -612,6 +639,10 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te
 	unsigned char cmd[6], buf[1];
 	unsigned char cmd[6], buf[1];
 	int err, amount;
 	int err, amount;
 
 
+	// Device is gone
+	if (modminer->usbinfo.nodev)
+		return clocknodev;
+
 	// Only do once if multiple shares per work or multiple reasons
 	// Only do once if multiple shares per work or multiple reasons
 	if (!state->new_work && !force)
 	if (!state->new_work && !force)
 		return clockoldwork;
 		return clockoldwork;
@@ -653,7 +684,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error writing set clock speed (%d:%d)",
 		applog(LOG_ERR, "%s%u: Error writing set clock speed (%d:%d)",
-			modminer->api->name, modminer->device_id, amount, err);
+			modminer->drv->name, modminer->device_id, amount, err);
 
 
 		return clocksetfail;
 		return clocksetfail;
 	}
 	}
@@ -662,7 +693,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error reading set clock speed (%d:%d)",
 		applog(LOG_ERR, "%s%u: Error reading set clock speed (%d:%d)",
-			modminer->api->name, modminer->device_id, amount, err);
+			modminer->drv->name, modminer->device_id, amount, err);
 
 
 		return clockreplyfail;
 		return clockreplyfail;
 	}
 	}
@@ -670,7 +701,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te
 	mutex_unlock(modminer->modminer_mutex);
 	mutex_unlock(modminer->modminer_mutex);
 
 
 	applog(LOG_WARNING, "%s%u: Set clock speed %sto %u",
 	applog(LOG_WARNING, "%s%u: Set clock speed %sto %u",
-			modminer->api->name, modminer->device_id,
+			modminer->drv->name, modminer->device_id,
 			(delta < 0) ? "down " : (delta > 0 ? "up " : ""),
 			(delta < 0) ? "down " : (delta > 0 ? "up " : ""),
 			modminer->clock);
 			modminer->clock);
 
 
@@ -691,7 +722,7 @@ static bool modminer_fpga_init(struct thr_info *thr)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error requesting USER code (%d:%d)",
 		applog(LOG_ERR, "%s%u: Error requesting USER code (%d:%d)",
-			modminer->api->name, modminer->device_id, amount, err);
+			modminer->drv->name, modminer->device_id, amount, err);
 
 
 		return false;
 		return false;
 	}
 	}
@@ -700,14 +731,14 @@ static bool modminer_fpga_init(struct thr_info *thr)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Error reading USER code (%d:%d)",
 		applog(LOG_ERR, "%s%u: Error reading USER code (%d:%d)",
-			modminer->api->name, modminer->device_id, amount, err);
+			modminer->drv->name, modminer->device_id, amount, err);
 
 
 		return false;
 		return false;
 	}
 	}
 
 
 	if (memcmp(buf, BISTREAM_USER_ID, 4)) {
 	if (memcmp(buf, BISTREAM_USER_ID, 4)) {
 		applog(LOG_ERR, "%s%u: FPGA not programmed",
 		applog(LOG_ERR, "%s%u: FPGA not programmed",
-			modminer->api->name, modminer->device_id);
+			modminer->drv->name, modminer->device_id);
 
 
 		if (!modminer_fpga_upload_bitstream(modminer))
 		if (!modminer_fpga_upload_bitstream(modminer))
 			return false;
 			return false;
@@ -717,7 +748,7 @@ static bool modminer_fpga_init(struct thr_info *thr)
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_DEBUG, "%s%u: FPGA is already programmed :)",
 		applog(LOG_DEBUG, "%s%u: FPGA is already programmed :)",
-			modminer->api->name, modminer->device_id);
+			modminer->drv->name, modminer->device_id);
 	}
 	}
 
 
 	modminer->clock = MODMINER_DEF_CLOCK;
 	modminer->clock = MODMINER_DEF_CLOCK;
@@ -740,24 +771,19 @@ static void get_modminer_statline_before(char *buf, struct cgpu_info *modminer)
 	strcat(buf, info);
 	strcat(buf, info);
 }
 }
 
 
-static bool modminer_prepare_next_work(struct modminer_fpga_state *state, struct work *work)
-{
-	char *midstate = state->next_work_cmd + 2;
-	char *taildata = midstate + 32;
-	if (!(memcmp(midstate, work->midstate, 32) || memcmp(taildata, work->data + 64, 12)))
-		return false;
-	memcpy(midstate, work->midstate, 32);
-	memcpy(taildata, work->data + 64, 12);
-	return true;
-}
-
-static bool modminer_start_work(struct thr_info *thr)
+static bool modminer_start_work(struct thr_info *thr, struct work *work)
 {
 {
 	struct cgpu_info *modminer = thr->cgpu;
 	struct cgpu_info *modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	int err, amount;
 	int err, amount;
+	char cmd[48];
 	bool sta;
 	bool sta;
 
 
+	cmd[0] = MODMINER_SEND_WORK;
+	cmd[1] = modminer->fpgaid;
+	memcpy(&cmd[2], work->midstate, 32);
+	memcpy(&cmd[34], work->data + 64, 12);
+
 	if (state->first_work.tv_sec == 0)
 	if (state->first_work.tv_sec == 0)
 		gettimeofday(&state->first_work, NULL);
 		gettimeofday(&state->first_work, NULL);
 
 
@@ -766,20 +792,16 @@ static bool modminer_start_work(struct thr_info *thr)
 
 
 	mutex_lock(modminer->modminer_mutex);
 	mutex_lock(modminer->modminer_mutex);
 
 
-	if ((err = usb_write(modminer, (char *)(state->next_work_cmd), 46, &amount, C_SENDWORK)) < 0 || amount != 46) {
-// TODO: err = LIBUSB_ERROR_NO_DEVICE means the MMQ disappeared
-// - need to delete it and rescan for it? (after a delay?)
-// but check all (4) disappeared
+	if ((err = usb_write(modminer, cmd, 46, &amount, C_SENDWORK)) < 0 || amount != 46) {
 		mutex_unlock(modminer->modminer_mutex);
 		mutex_unlock(modminer->modminer_mutex);
 
 
 		applog(LOG_ERR, "%s%u: Start work failed (%d:%d)",
 		applog(LOG_ERR, "%s%u: Start work failed (%d:%d)",
-			modminer->api->name, modminer->device_id, amount, err);
+			modminer->drv->name, modminer->device_id, amount, err);
 
 
 		return false;
 		return false;
 	}
 	}
 
 
 	gettimeofday(&state->tv_workstart, NULL);
 	gettimeofday(&state->tv_workstart, NULL);
-	state->hashes = 0;
 
 
 	sta = get_status(modminer, "start work", C_SENDWORKSTATUS);
 	sta = get_status(modminer, "start work", C_SENDWORKSTATUS);
 
 
@@ -799,6 +821,10 @@ static void check_temperature(struct thr_info *thr)
 	int tbytes, tamount;
 	int tbytes, tamount;
 	int amount;
 	int amount;
 
 
+	// Device is gone
+	if (modminer->usbinfo.nodev)
+		return;
+
 	if (state->one_byte_temp) {
 	if (state->one_byte_temp) {
 		cmd[0] = MODMINER_TEMP1;
 		cmd[0] = MODMINER_TEMP1;
 		tbytes = 1;
 		tbytes = 1;
@@ -827,14 +853,14 @@ static void check_temperature(struct thr_info *thr)
 			if (modminer->temp < MODMINER_RECOVER_TEMP) {
 			if (modminer->temp < MODMINER_RECOVER_TEMP) {
 				state->overheated = false;
 				state->overheated = false;
 				applog(LOG_WARNING, "%s%u: Recovered, temp less than (%.1f) now %.3f",
 				applog(LOG_WARNING, "%s%u: Recovered, temp less than (%.1f) now %.3f",
-					modminer->api->name, modminer->device_id,
+					modminer->drv->name, modminer->device_id,
 					MODMINER_RECOVER_TEMP, modminer->temp);
 					MODMINER_RECOVER_TEMP, modminer->temp);
 			}
 			}
 		}
 		}
 		else if (modminer->temp >= MODMINER_OVERHEAT_TEMP) {
 		else if (modminer->temp >= MODMINER_OVERHEAT_TEMP) {
 			if (modminer->temp >= MODMINER_CUTOFF_TEMP) {
 			if (modminer->temp >= MODMINER_CUTOFF_TEMP) {
 				applog(LOG_WARNING, "%s%u: Hit thermal cutoff limit! (%.1f) at %.3f",
 				applog(LOG_WARNING, "%s%u: Hit thermal cutoff limit! (%.1f) at %.3f",
-					modminer->api->name, modminer->device_id,
+					modminer->drv->name, modminer->device_id,
 					MODMINER_CUTOFF_TEMP, modminer->temp);
 					MODMINER_CUTOFF_TEMP, modminer->temp);
 
 
 				modminer_delta_clock(thr, MODMINER_CLOCK_CUTOFF, true, false);
 				modminer_delta_clock(thr, MODMINER_CLOCK_CUTOFF, true, false);
@@ -842,7 +868,7 @@ static void check_temperature(struct thr_info *thr)
 				dev_error(modminer, REASON_DEV_THERMAL_CUTOFF);
 				dev_error(modminer, REASON_DEV_THERMAL_CUTOFF);
 			} else {
 			} else {
 				applog(LOG_WARNING, "%s%u: Overheat limit (%.1f) reached %.3f",
 				applog(LOG_WARNING, "%s%u: Overheat limit (%.1f) reached %.3f",
-					modminer->api->name, modminer->device_id,
+					modminer->drv->name, modminer->device_id,
 					MODMINER_OVERHEAT_TEMP, modminer->temp);
 					MODMINER_OVERHEAT_TEMP, modminer->temp);
 
 
 				// If it's defined to be 0 then don't call modminer_delta_clock()
 				// If it's defined to be 0 then don't call modminer_delta_clock()
@@ -869,11 +895,10 @@ static const double processtime = 17.0;
 // 160Mhz is 26.84 - when overheated ensure we don't throw away shares
 // 160Mhz is 26.84 - when overheated ensure we don't throw away shares
 static const double overheattime = 26.9;
 static const double overheattime = 26.9;
 
 
-static uint64_t modminer_process_results(struct thr_info *thr)
+static uint64_t modminer_process_results(struct thr_info *thr, struct work *work)
 {
 {
 	struct cgpu_info *modminer = thr->cgpu;
 	struct cgpu_info *modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	struct modminer_fpga_state *state = thr->cgpu_data;
-	struct work *work = &state->running_work;
 	struct timeval now;
 	struct timeval now;
 	char cmd[2];
 	char cmd[2];
 	uint32_t nonce;
 	uint32_t nonce;
@@ -883,6 +908,10 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 	double timeout;
 	double timeout;
 	int temploop;
 	int temploop;
 
 
+	// Device is gone
+	if (modminer->usbinfo.nodev)
+		return -1;
+
 	// If we are overheated it will just keep checking for results
 	// If we are overheated it will just keep checking for results
 	// since we can't stop the work
 	// since we can't stop the work
 	// The next work will not start until the temp drops
 	// The next work will not start until the temp drops
@@ -893,23 +922,20 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 
 
 	timeoutloop = 0;
 	timeoutloop = 0;
 	temploop = 0;
 	temploop = 0;
-	while (1) {
+	while (0x80085) {
 		mutex_lock(modminer->modminer_mutex);
 		mutex_lock(modminer->modminer_mutex);
 		if ((err = usb_write(modminer, cmd, 2, &amount, C_REQUESTWORKSTATUS)) < 0 || amount != 2) {
 		if ((err = usb_write(modminer, cmd, 2, &amount, C_REQUESTWORKSTATUS)) < 0 || amount != 2) {
-// TODO: err = LIBUSB_ERROR_NO_DEVICE means the MMQ disappeared
-// - need to delete it and rescan for it? (after a delay?)
-// but check all (4) disappeared
 			mutex_unlock(modminer->modminer_mutex);
 			mutex_unlock(modminer->modminer_mutex);
 
 
 			// timeoutloop never resets so the timeouts can't
 			// timeoutloop never resets so the timeouts can't
 			// accumulate much during a single item of work
 			// accumulate much during a single item of work
-			if (err == LIBUSB_ERROR_TIMEOUT && ++timeoutloop < 10) {
+			if (err == LIBUSB_ERROR_TIMEOUT && ++timeoutloop < 5) {
 				state->timeout_fail++;
 				state->timeout_fail++;
 				goto tryagain;
 				goto tryagain;
 			}
 			}
 
 
 			applog(LOG_ERR, "%s%u: Error sending (get nonce) (%d:%d)",
 			applog(LOG_ERR, "%s%u: Error sending (get nonce) (%d:%d)",
-				modminer->api->name, modminer->device_id, amount, err);
+				modminer->drv->name, modminer->device_id, amount, err);
 
 
 			return -1;
 			return -1;
 		}
 		}
@@ -936,7 +962,7 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 			}
 			}
 
 
 			applog(LOG_ERR, "%s%u: Error reading (get nonce) (%d:%d)",
 			applog(LOG_ERR, "%s%u: Error reading (get nonce) (%d:%d)",
-				modminer->api->name, modminer->device_id, amount+amount2, err);
+				modminer->drv->name, modminer->device_id, amount+amount2, err);
 		}
 		}
 
 
 		if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) {
 		if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) {
@@ -975,7 +1001,7 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 				if (state->death_stage_one) {
 				if (state->death_stage_one) {
 					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true);
 					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true);
 					applog(LOG_ERR, "%s%u: DEATH clock down",
 					applog(LOG_ERR, "%s%u: DEATH clock down",
-						modminer->api->name, modminer->device_id);
+						modminer->drv->name, modminer->device_id);
 
 
 					// reset the death info and DISABLE it
 					// reset the death info and DISABLE it
 					state->last_nonce.tv_sec = 0;
 					state->last_nonce.tv_sec = 0;
@@ -985,7 +1011,7 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 				} else {
 				} else {
 					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true);
 					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true);
 					applog(LOG_ERR, "%s%u: death clock down",
 					applog(LOG_ERR, "%s%u: death clock down",
-						modminer->api->name, modminer->device_id);
+						modminer->drv->name, modminer->device_id);
 
 
 					state->death_stage_one = true;
 					state->death_stage_one = true;
 				}
 				}
@@ -998,8 +1024,8 @@ tryagain:
 			break;
 			break;
 
 
 		if (state->overheated == true) {
 		if (state->overheated == true) {
-			// don't check every time
-			if (++temploop > 30) {
+			// don't check every time (every ~1/2 sec)
+			if (++temploop > 4) {
 				check_temperature(thr);
 				check_temperature(thr);
 				temploop = 0;
 				temploop = 0;
 			}
 			}
@@ -1015,7 +1041,8 @@ tryagain:
 		if (tdiff(&now, &state->tv_workstart) > timeout)
 		if (tdiff(&now, &state->tv_workstart) > timeout)
 			break;
 			break;
 
 
-		nmsleep(10);
+		// 1/10th sec to lower CPU usage
+		nmsleep(100);
 		if (work_restart(thr))
 		if (work_restart(thr))
 			break;
 			break;
 	}
 	}
@@ -1029,35 +1056,37 @@ tryagain:
 	// Overheat will complete the nonce range
 	// Overheat will complete the nonce range
 	if (hashes > 0xffffffff)
 	if (hashes > 0xffffffff)
 		hashes = 0xffffffff;
 		hashes = 0xffffffff;
-	else
-	if (hashes <= state->hashes)
-		hashes = 1;
-	else
-		hashes -= state->hashes;
-	state->hashes += hashes;
+
+	work->blk.nonce = 0xffffffff;
+
 	return hashes;
 	return hashes;
 }
 }
 
 
 static int64_t modminer_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce)
 static int64_t modminer_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce)
 {
 {
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	struct modminer_fpga_state *state = thr->cgpu_data;
-	int64_t hashes = 0;
-	bool startwork;
 	struct timeval tv1, tv2;
 	struct timeval tv1, tv2;
+	int64_t hashes;
+
+	// Device is gone
+	if (thr->cgpu->usbinfo.nodev)
+		return -1;
 
 
 	// Don't start new work if overheated
 	// Don't start new work if overheated
 	if (state->overheated == true) {
 	if (state->overheated == true) {
 		gettimeofday(&tv1, NULL);
 		gettimeofday(&tv1, NULL);
-		if (state->work_running)
-			state->work_running = false;
 
 
 		while (state->overheated == true) {
 		while (state->overheated == true) {
 			check_temperature(thr);
 			check_temperature(thr);
 
 
+			// Device is gone
+			if (thr->cgpu->usbinfo.nodev)
+				return -1;
+
 			if (state->overheated == true) {
 			if (state->overheated == true) {
 				gettimeofday(&tv2, NULL);
 				gettimeofday(&tv2, NULL);
 
 
-				// give up on this work item
+				// give up on this work item after 30s
 				if (work_restart(thr) || tdiff(&tv2, &tv1) > 30)
 				if (work_restart(thr) || tdiff(&tv2, &tv1) > 30)
 					return 0;
 					return 0;
 
 
@@ -1067,27 +1096,13 @@ static int64_t modminer_scanhash(struct thr_info *thr, struct work *work, int64_
 		}
 		}
 	}
 	}
 
 
-	startwork = modminer_prepare_next_work(state, work);
-	if (state->work_running) {
-		hashes = modminer_process_results(thr);
-		if (hashes == -1)
-			return hashes;
-
-		if (work_restart(thr)) {
-			state->work_running = false;
-			return 0;
-		}
-	} else
-		state->work_running = true;
+	if (!modminer_start_work(thr, work))
+		return -1;
 
 
-	if (startwork) {
-		if (!modminer_start_work(thr))
-			return -1;
-		__copy_work(&state->running_work, work);
-	}
+	hashes = modminer_process_results(thr, work);
+	if (hashes == -1)
+		return hashes;
 
 
-	// This is intentionally early
-	work->blk.nonce += hashes;
 	return hashes;
 	return hashes;
 }
 }
 
 
@@ -1141,10 +1156,11 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char
 	return replybuf;
 	return replybuf;
 }
 }
 
 
-struct device_api modminer_api = {
-	.dname = "modminer",
+struct device_drv modminer_drv = {
+	.drv_id = DRIVER_MODMINER,
+	.dname = "ModMiner",
 	.name = "MMQ",
 	.name = "MMQ",
-	.api_detect = modminer_detect,
+	.drv_detect = modminer_detect,
 	.get_statline_before = get_modminer_statline_before,
 	.get_statline_before = get_modminer_statline_before,
 	.set_device = modminer_set_device,
 	.set_device = modminer_set_device,
 	.thread_prepare = modminer_fpga_prepare,
 	.thread_prepare = modminer_fpga_prepare,

+ 24 - 34
driver-opencl.c

@@ -50,24 +50,24 @@ extern int gpur_thr_id;
 extern bool opt_noadl;
 extern bool opt_noadl;
 extern bool have_opencl;
 extern bool have_opencl;
 
 
-
-
 extern void *miner_thread(void *userdata);
 extern void *miner_thread(void *userdata);
 extern int dev_from_id(int thr_id);
 extern int dev_from_id(int thr_id);
 extern void tailsprintf(char *f, const char *fmt, ...);
 extern void tailsprintf(char *f, const char *fmt, ...);
 extern void wlog(const char *f, ...);
 extern void wlog(const char *f, ...);
 extern void decay_time(double *f, double fadd);
 extern void decay_time(double *f, double fadd);
 
 
-
 /**********************************************/
 /**********************************************/
 
 
+#ifdef HAVE_OPENCL
+struct device_drv opencl_drv;
+#endif
+
 #ifdef HAVE_ADL
 #ifdef HAVE_ADL
 extern float gpu_temp(int gpu);
 extern float gpu_temp(int gpu);
 extern int gpu_fanspeed(int gpu);
 extern int gpu_fanspeed(int gpu);
 extern int gpu_fanpercent(int gpu);
 extern int gpu_fanpercent(int gpu);
 #endif
 #endif
 
 
-
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
 char *set_vector(char *arg)
 char *set_vector(char *arg)
 {
 {
@@ -591,28 +591,20 @@ char *set_intensity(char *arg)
 
 
 	return NULL;
 	return NULL;
 }
 }
-#endif
-
-
-#ifdef HAVE_OPENCL
-struct device_api opencl_api;
 
 
 char *print_ndevs_and_exit(int *ndevs)
 char *print_ndevs_and_exit(int *ndevs)
 {
 {
 	opt_log_output = true;
 	opt_log_output = true;
-	opencl_api.api_detect();
+	opencl_drv.drv_detect();
 	clear_adl(*ndevs);
 	clear_adl(*ndevs);
 	applog(LOG_INFO, "%i GPU devices max detected", *ndevs);
 	applog(LOG_INFO, "%i GPU devices max detected", *ndevs);
 	exit(*ndevs);
 	exit(*ndevs);
 }
 }
 #endif
 #endif
 
 
-
 struct cgpu_info gpus[MAX_GPUDEVICES]; /* Maximum number apparently possible */
 struct cgpu_info gpus[MAX_GPUDEVICES]; /* Maximum number apparently possible */
 struct cgpu_info *cpus;
 struct cgpu_info *cpus;
 
 
-
-
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
 
 
 /* In dynamic mode, only the first thread of each device will be in use.
 /* In dynamic mode, only the first thread of each device will be in use.
@@ -624,8 +616,9 @@ void pause_dynamic_threads(int gpu)
 	int i;
 	int i;
 
 
 	for (i = 1; i < cgpu->threads; i++) {
 	for (i = 1; i < cgpu->threads; i++) {
-		struct thr_info *thr = &thr_info[i];
+		struct thr_info *thr;
 
 
+		thr = get_thread(i);
 		if (!thr->pause && cgpu->dynamic) {
 		if (!thr->pause && cgpu->dynamic) {
 			applog(LOG_WARNING, "Disabling extra threads due to dynamic mode.");
 			applog(LOG_WARNING, "Disabling extra threads due to dynamic mode.");
 			applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval");
 			applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval");
@@ -637,9 +630,6 @@ void pause_dynamic_threads(int gpu)
 	}
 	}
 }
 }
 
 
-
-struct device_api opencl_api;
-
 #endif /* HAVE_OPENCL */
 #endif /* HAVE_OPENCL */
 
 
 #if defined(HAVE_OPENCL) && defined(HAVE_CURSES)
 #if defined(HAVE_OPENCL) && defined(HAVE_CURSES)
@@ -716,7 +706,7 @@ retry:
 		else
 		else
 			wlog("%d\n", gpus[gpu].intensity);
 			wlog("%d\n", gpus[gpu].intensity);
 		for (i = 0; i < mining_threads; i++) {
 		for (i = 0; i < mining_threads; i++) {
-			thr = &thr_info[i];
+			thr = get_thread(i);
 			if (thr->cgpu != cgpu)
 			if (thr->cgpu != cgpu)
 				continue;
 				continue;
 			get_datestamp(checkin, &thr->last);
 			get_datestamp(checkin, &thr->last);
@@ -771,9 +761,9 @@ retry:
 		}
 		}
 		gpus[selected].deven = DEV_ENABLED;
 		gpus[selected].deven = DEV_ENABLED;
 		for (i = 0; i < mining_threads; ++i) {
 		for (i = 0; i < mining_threads; ++i) {
-			thr = &thr_info[i];
+			thr = get_thread(i);
 			cgpu = thr->cgpu;
 			cgpu = thr->cgpu;
-			if (cgpu->api != &opencl_api)
+			if (cgpu->drv->drv_id != DRIVER_OPENCL)
 				continue;
 				continue;
 			if (dev_from_id(i) != selected)
 			if (dev_from_id(i) != selected)
 				continue;
 				continue;
@@ -1158,14 +1148,14 @@ select_cgpu:
 	gpu = cgpu->device_id;
 	gpu = cgpu->device_id;
 
 
 	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
 	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
-		thr = &thr_info[thr_id];
+		thr = get_thread(thr_id);
 		cgpu = thr->cgpu;
 		cgpu = thr->cgpu;
-		if (cgpu->api != &opencl_api)
+		if (cgpu->drv->drv_id != DRIVER_OPENCL)
 			continue;
 			continue;
 		if (dev_from_id(thr_id) != gpu)
 		if (dev_from_id(thr_id) != gpu)
 			continue;
 			continue;
 
 
-		thr = &thr_info[thr_id];
+		thr = get_thread(thr_id);
 		if (!thr) {
 		if (!thr) {
 			applog(LOG_WARNING, "No reference to thread %d exists", thr_id);
 			applog(LOG_WARNING, "No reference to thread %d exists", thr_id);
 			continue;
 			continue;
@@ -1183,9 +1173,9 @@ select_cgpu:
 	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
 	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
 		int virtual_gpu;
 		int virtual_gpu;
 
 
-		thr = &thr_info[thr_id];
+		thr = get_thread(thr_id);
 		cgpu = thr->cgpu;
 		cgpu = thr->cgpu;
-		if (cgpu->api != &opencl_api)
+		if (cgpu->drv->drv_id != DRIVER_OPENCL)
 			continue;
 			continue;
 		if (dev_from_id(thr_id) != gpu)
 		if (dev_from_id(thr_id) != gpu)
 			continue;
 			continue;
@@ -1220,9 +1210,9 @@ select_cgpu:
 	get_datestamp(cgpu->init, &now);
 	get_datestamp(cgpu->init, &now);
 
 
 	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
 	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
-		thr = &thr_info[thr_id];
+		thr = get_thread(thr_id);
 		cgpu = thr->cgpu;
 		cgpu = thr->cgpu;
-		if (cgpu->api != &opencl_api)
+		if (cgpu->drv->drv_id != DRIVER_OPENCL)
 			continue;
 			continue;
 		if (dev_from_id(thr_id) != gpu)
 		if (dev_from_id(thr_id) != gpu)
 			continue;
 			continue;
@@ -1243,8 +1233,6 @@ void *reinit_gpu(__maybe_unused void *userdata)
 
 
 
 
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
-struct device_api opencl_api;
-
 static void opencl_detect()
 static void opencl_detect()
 {
 {
 	int i;
 	int i;
@@ -1263,7 +1251,7 @@ static void opencl_detect()
 
 
 		cgpu = &gpus[i];
 		cgpu = &gpus[i];
 		cgpu->deven = DEV_ENABLED;
 		cgpu->deven = DEV_ENABLED;
-		cgpu->api = &opencl_api;
+		cgpu->drv = &opencl_drv;
 		cgpu->device_id = i;
 		cgpu->device_id = i;
 		cgpu->threads = opt_g_threads;
 		cgpu->threads = opt_g_threads;
 		cgpu->virtual_gpu = i;
 		cgpu->virtual_gpu = i;
@@ -1276,7 +1264,7 @@ static void opencl_detect()
 
 
 static void reinit_opencl_device(struct cgpu_info *gpu)
 static void reinit_opencl_device(struct cgpu_info *gpu)
 {
 {
-	tq_push(thr_info[gpur_thr_id].q, gpu);
+	tq_push(control_thr[gpur_thr_id].q, gpu);
 }
 }
 
 
 #ifdef HAVE_ADL
 #ifdef HAVE_ADL
@@ -1299,7 +1287,8 @@ static void get_opencl_statline_before(char *buf, struct cgpu_info *gpu)
 		else
 		else
 			tailsprintf(buf, "        ");
 			tailsprintf(buf, "        ");
 		tailsprintf(buf, "| ");
 		tailsprintf(buf, "| ");
-	}
+	} else
+		gpu->drv->get_statline_before = &blank_get_statline_before;
 }
 }
 #endif
 #endif
 
 
@@ -1570,10 +1559,11 @@ static void opencl_thread_shutdown(struct thr_info *thr)
 	clReleaseContext(clState->context);
 	clReleaseContext(clState->context);
 }
 }
 
 
-struct device_api opencl_api = {
+struct device_drv opencl_drv = {
+	.drv_id = DRIVER_OPENCL,
 	.dname = "opencl",
 	.dname = "opencl",
 	.name = "GPU",
 	.name = "GPU",
-	.api_detect = opencl_detect,
+	.drv_detect = opencl_detect,
 	.reinit_device = reinit_opencl_device,
 	.reinit_device = reinit_opencl_device,
 #ifdef HAVE_ADL
 #ifdef HAVE_ADL
 	.get_statline_before = get_opencl_statline_before,
 	.get_statline_before = get_opencl_statline_before,

+ 1 - 1
driver-opencl.h

@@ -30,6 +30,6 @@ extern void pause_dynamic_threads(int gpu);
 extern bool have_opencl;
 extern bool have_opencl;
 extern int opt_platform_id;
 extern int opt_platform_id;
 
 
-extern struct device_api opencl_api;
+extern struct device_drv opencl_drv;
 
 
 #endif /* __DEVICE_GPU_H__ */
 #endif /* __DEVICE_GPU_H__ */

+ 9 - 6
driver-ztex.c

@@ -30,7 +30,7 @@
 
 
 #define GOLDEN_BACKLOG 5
 #define GOLDEN_BACKLOG 5
 
 
-struct device_api ztex_api;
+struct device_drv ztex_drv;
 
 
 // Forward declarations
 // Forward declarations
 static void ztex_disable(struct thr_info* thr);
 static void ztex_disable(struct thr_info* thr);
@@ -68,7 +68,7 @@ static void ztex_detect(void)
 
 
 	for (i = 0; i < cnt; i++) {
 	for (i = 0; i < cnt; i++) {
 		ztex = calloc(1, sizeof(struct cgpu_info));
 		ztex = calloc(1, sizeof(struct cgpu_info));
-		ztex->api = &ztex_api;
+		ztex->drv = &ztex_drv;
 		ztex->device_ztex = ztex_devices[i]->dev;
 		ztex->device_ztex = ztex_devices[i]->dev;
 		ztex->threads = 1;
 		ztex->threads = 1;
 		ztex->device_ztex->fpgaNum = 0;
 		ztex->device_ztex->fpgaNum = 0;
@@ -82,7 +82,7 @@ static void ztex_detect(void)
 
 
 		for (j = 1; j < fpgacount; j++) {
 		for (j = 1; j < fpgacount; j++) {
 			ztex = calloc(1, sizeof(struct cgpu_info));
 			ztex = calloc(1, sizeof(struct cgpu_info));
-			ztex->api = &ztex_api;
+			ztex->drv = &ztex_drv;
 			ztex_slave = calloc(1, sizeof(struct libztex_device));
 			ztex_slave = calloc(1, sizeof(struct libztex_device));
 			memcpy(ztex_slave, ztex_devices[i]->dev, sizeof(struct libztex_device));
 			memcpy(ztex_slave, ztex_devices[i]->dev, sizeof(struct libztex_device));
 			ztex->device_ztex = ztex_slave;
 			ztex->device_ztex = ztex_slave;
@@ -389,15 +389,18 @@ static void ztex_shutdown(struct thr_info *thr)
 
 
 static void ztex_disable(struct thr_info *thr)
 static void ztex_disable(struct thr_info *thr)
 {
 {
+	struct cgpu_info *cgpu;
+
 	applog(LOG_ERR, "%s: Disabling!", thr->cgpu->device_ztex->repr);
 	applog(LOG_ERR, "%s: Disabling!", thr->cgpu->device_ztex->repr);
-	devices[thr->cgpu->device_id]->deven = DEV_DISABLED;
+	cgpu = get_devices(thr->cgpu->device_id);
+	cgpu->deven = DEV_DISABLED;
 	ztex_shutdown(thr);
 	ztex_shutdown(thr);
 }
 }
 
 
-struct device_api ztex_api = {
+struct device_drv ztex_drv = {
 	.dname = "ztex",
 	.dname = "ztex",
 	.name = "ZTX",
 	.name = "ZTX",
-	.api_detect = ztex_detect,
+	.drv_detect = ztex_detect,
 	.get_statline_before = ztex_statline_before,
 	.get_statline_before = ztex_statline_before,
 	.thread_prepare = ztex_prepare,
 	.thread_prepare = ztex_prepare,
 	.scanhash = ztex_scanhash,
 	.scanhash = ztex_scanhash,

+ 1 - 1
findnonce.c

@@ -204,7 +204,7 @@ static void *postcalc_hash(void *userdata)
 	 * end of the res[] array */
 	 * end of the res[] array */
 	if (unlikely(pcd->res[FOUND] & ~FOUND)) {
 	if (unlikely(pcd->res[FOUND] & ~FOUND)) {
 		applog(LOG_WARNING, "%s%d: invalid nonce count - HW error",
 		applog(LOG_WARNING, "%s%d: invalid nonce count - HW error",
-				thr->cgpu->api->name, thr->cgpu->device_id);
+				thr->cgpu->drv->name, thr->cgpu->device_id);
 		hw_errors++;
 		hw_errors++;
 		thr->cgpu->hw_errors++;
 		thr->cgpu->hw_errors++;
 		pcd->res[FOUND] &= FOUND;
 		pcd->res[FOUND] &= FOUND;

+ 5 - 5
fpgautils.c

@@ -104,14 +104,14 @@ int serial_autodetect_devserial(__maybe_unused detectone_func_t detectone, __may
 #endif
 #endif
 }
 }
 
 
-int _serial_detect(struct device_api *api, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto)
+int _serial_detect(struct device_drv *drv, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto)
 {
 {
 	struct string_elist *iter, *tmp;
 	struct string_elist *iter, *tmp;
 	const char *dev, *colon;
 	const char *dev, *colon;
 	bool inhibitauto = false;
 	bool inhibitauto = false;
 	char found = 0;
 	char found = 0;
-	size_t namel = strlen(api->name);
-	size_t dnamel = strlen(api->dname);
+	size_t namel = strlen(drv->name);
+	size_t dnamel = strlen(drv->dname);
 
 
 	list_for_each_entry_safe(iter, tmp, &scan_devices, list) {
 	list_for_each_entry_safe(iter, tmp, &scan_devices, list) {
 		dev = iter->string;
 		dev = iter->string;
@@ -119,8 +119,8 @@ int _serial_detect(struct device_api *api, detectone_func_t detectone, autoscan_
 			size_t idlen = colon - dev;
 			size_t idlen = colon - dev;
 
 
 			// allow either name:device or dname:device
 			// allow either name:device or dname:device
-			if ((idlen != namel || strncasecmp(dev, api->name, idlen))
-			&&  (idlen != dnamel || strncasecmp(dev, api->dname, idlen)))
+			if ((idlen != namel || strncasecmp(dev, drv->name, idlen))
+			&&  (idlen != dnamel || strncasecmp(dev, drv->dname, idlen)))
 				continue;
 				continue;
 
 
 			dev = colon + 1;
 			dev = colon + 1;

+ 7 - 7
fpgautils.h

@@ -16,13 +16,13 @@
 typedef bool(*detectone_func_t)(const char*);
 typedef bool(*detectone_func_t)(const char*);
 typedef int(*autoscan_func_t)();
 typedef int(*autoscan_func_t)();
 
 
-extern int _serial_detect(struct device_api *api, detectone_func_t, autoscan_func_t, bool force_autoscan);
-#define serial_detect_fauto(api, detectone, autoscan)  \
-	_serial_detect(api, detectone, autoscan, true)
-#define serial_detect_auto(api, detectone, autoscan)  \
-	_serial_detect(api, detectone, autoscan, false)
-#define serial_detect(api, detectone)  \
-	_serial_detect(api, detectone, NULL, false)
+extern int _serial_detect(struct device_drv *drv, detectone_func_t, autoscan_func_t, bool force_autoscan);
+#define serial_detect_fauto(drv, detectone, autoscan)  \
+	_serial_detect(drv, detectone, autoscan, true)
+#define serial_detect_auto(drv, detectone, autoscan)  \
+	_serial_detect(drv, detectone, autoscan, false)
+#define serial_detect(drv, detectone)  \
+	_serial_detect(drv, detectone, NULL, false)
 extern int serial_autodetect_devserial(detectone_func_t, const char *prodname);
 extern int serial_autodetect_devserial(detectone_func_t, const char *prodname);
 extern int serial_autodetect_udev(detectone_func_t, const char *prodname);
 extern int serial_autodetect_udev(detectone_func_t, const char *prodname);
 
 

+ 42 - 8
miner.h

@@ -114,7 +114,7 @@ static inline int fsync (int fd)
   #include "libztex.h"
   #include "libztex.h"
 #endif
 #endif
 
 
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
   #include "usbutils.h"
   #include "usbutils.h"
 #endif
 #endif
 
 
@@ -196,6 +196,15 @@ static inline int fsync (int fd)
 #endif
 #endif
 #endif
 #endif
 
 
+enum drv_driver {
+	DRIVER_OPENCL,
+	DRIVER_ICARUS,
+	DRIVER_BITFORCE,
+	DRIVER_MODMINER,
+	DRIVER_ZTEX,
+	DRIVER_CPU,
+};
+
 enum alive {
 enum alive {
 	LIFE_WELL,
 	LIFE_WELL,
 	LIFE_SICK,
 	LIFE_SICK,
@@ -259,16 +268,20 @@ struct gpu_adl {
 };
 };
 #endif
 #endif
 
 
+extern void blank_get_statline_before(char *buf, struct cgpu_info __maybe_unused *cgpu);
+
 struct api_data;
 struct api_data;
 struct thr_info;
 struct thr_info;
 struct work;
 struct work;
 
 
-struct device_api {
+struct device_drv {
+	enum drv_driver drv_id;
+
 	char *dname;
 	char *dname;
 	char *name;
 	char *name;
 
 
-	// API-global functions
-	void (*api_detect)();
+	// DRV-global functions
+	void (*drv_detect)();
 
 
 	// Device-specific functions
 	// Device-specific functions
 	void (*reinit_device)(struct cgpu_info *);
 	void (*reinit_device)(struct cgpu_info *);
@@ -288,8 +301,13 @@ struct device_api {
 	void (*hw_error)(struct thr_info *);
 	void (*hw_error)(struct thr_info *);
 	void (*thread_shutdown)(struct thr_info *);
 	void (*thread_shutdown)(struct thr_info *);
 	void (*thread_enable)(struct thr_info *);
 	void (*thread_enable)(struct thr_info *);
+
+	// Does it need to be free()d?
+	bool copy;
 };
 };
 
 
+extern struct device_drv *copy_drv(struct device_drv*);
+
 enum dev_enable {
 enum dev_enable {
 	DEV_ENABLED,
 	DEV_ENABLED,
 	DEV_DISABLED,
 	DEV_DISABLED,
@@ -359,13 +377,15 @@ struct cgminer_pool_stats {
 	uint32_t max_diff_count;
 	uint32_t max_diff_count;
 	uint64_t times_sent;
 	uint64_t times_sent;
 	uint64_t bytes_sent;
 	uint64_t bytes_sent;
+	uint64_t net_bytes_sent;
 	uint64_t times_received;
 	uint64_t times_received;
 	uint64_t bytes_received;
 	uint64_t bytes_received;
+	uint64_t net_bytes_received;
 };
 };
 
 
 struct cgpu_info {
 struct cgpu_info {
 	int cgminer_id;
 	int cgminer_id;
-	struct device_api *api;
+	struct device_drv *drv;
 	int device_id;
 	int device_id;
 	char *name;
 	char *name;
 	char *device_path;
 	char *device_path;
@@ -374,13 +394,17 @@ struct cgpu_info {
 #ifdef USE_ZTEX
 #ifdef USE_ZTEX
 		struct libztex_device *device_ztex;
 		struct libztex_device *device_ztex;
 #endif
 #endif
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 		struct cg_usb_device *usbdev;
 		struct cg_usb_device *usbdev;
 #endif
 #endif
+#ifdef USE_ICARUS
 		int device_fd;
 		int device_fd;
+#endif
 	};
 	};
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
+	struct cg_usb_info usbinfo;
+#endif
 #ifdef USE_MODMINER
 #ifdef USE_MODMINER
-	int usbstat;
 	char fpgaid;
 	char fpgaid;
 	unsigned char clock;
 	unsigned char clock;
 	pthread_mutex_t *modminer_mutex;
 	pthread_mutex_t *modminer_mutex;
@@ -715,6 +739,8 @@ extern pthread_mutex_t cgusb_lock;
 extern pthread_mutex_t hash_lock;
 extern pthread_mutex_t hash_lock;
 extern pthread_mutex_t console_lock;
 extern pthread_mutex_t console_lock;
 extern pthread_mutex_t ch_lock;
 extern pthread_mutex_t ch_lock;
+extern pthread_mutex_t mining_thr_lock;
+extern pthread_mutex_t devices_lock;
 
 
 extern pthread_mutex_t restart_lock;
 extern pthread_mutex_t restart_lock;
 extern pthread_cond_t restart_cond;
 extern pthread_cond_t restart_cond;
@@ -740,6 +766,7 @@ extern void api(int thr_id);
 extern struct pool *current_pool(void);
 extern struct pool *current_pool(void);
 extern int enabled_pools;
 extern int enabled_pools;
 extern bool detect_stratum(struct pool *pool, char *url);
 extern bool detect_stratum(struct pool *pool, char *url);
+extern void print_summary(void);
 extern struct pool *add_pool(void);
 extern struct pool *add_pool(void);
 extern void add_pool_details(struct pool *pool, bool live, char *url, char *user, char *pass);
 extern void add_pool_details(struct pool *pool, bool live, char *url, char *user, char *pass);
 
 
@@ -755,6 +782,7 @@ extern void add_pool_details(struct pool *pool, bool live, char *url, char *user
 #define _MAX_INTENSITY_STR "14"
 #define _MAX_INTENSITY_STR "14"
 #endif
 #endif
 
 
+extern bool hotplug_mode;
 extern struct list_head scan_devices;
 extern struct list_head scan_devices;
 extern int nDevs;
 extern int nDevs;
 extern int opt_n_threads;
 extern int opt_n_threads;
@@ -762,7 +790,8 @@ extern int num_processors;
 extern int hw_errors;
 extern int hw_errors;
 extern bool use_syslog;
 extern bool use_syslog;
 extern bool opt_quiet;
 extern bool opt_quiet;
-extern struct thr_info *thr_info;
+extern struct thr_info *control_thr;
+extern struct thr_info **mining_thr;
 extern struct cgpu_info gpus[MAX_GPUDEVICES];
 extern struct cgpu_info gpus[MAX_GPUDEVICES];
 extern int gpu_threads;
 extern int gpu_threads;
 #ifdef USE_SCRYPT
 #ifdef USE_SCRYPT
@@ -926,6 +955,7 @@ struct pool {
 
 
 	time_t last_share_time;
 	time_t last_share_time;
 	double last_share_diff;
 	double last_share_diff;
+	uint64_t best_diff;
 
 
 	struct cgminer_stats cgminer_stats;
 	struct cgminer_stats cgminer_stats;
 	struct cgminer_pool_stats cgminer_pool_stats;
 	struct cgminer_pool_stats cgminer_pool_stats;
@@ -1065,6 +1095,8 @@ extern void kill_work(void);
 extern void switch_pools(struct pool *selected);
 extern void switch_pools(struct pool *selected);
 extern void remove_pool(struct pool *pool);
 extern void remove_pool(struct pool *pool);
 extern void write_config(FILE *fcfg);
 extern void write_config(FILE *fcfg);
+extern void zero_bestshare(void);
+extern void zero_stats(void);
 extern void default_save_file(char *filename);
 extern void default_save_file(char *filename);
 extern bool log_curses_only(int prio, const char *f, va_list ap);
 extern bool log_curses_only(int prio, const char *f, va_list ap);
 extern void clear_logwin(void);
 extern void clear_logwin(void);
@@ -1082,6 +1114,8 @@ extern void clean_work(struct work *work);
 extern void free_work(struct work *work);
 extern void free_work(struct work *work);
 extern void __copy_work(struct work *work, struct work *base_work);
 extern void __copy_work(struct work *work, struct work *base_work);
 extern struct work *copy_work(struct work *base_work);
 extern struct work *copy_work(struct work *base_work);
+extern struct thr_info *get_thread(int thr_id);
+extern struct cgpu_info *get_devices(int id);
 
 
 enum api_data_type {
 enum api_data_type {
 	API_ESCAPE,
 	API_ESCAPE,

+ 239 - 50
miner.php

@@ -8,7 +8,7 @@ global $checklastshare, $poolinputs, $hidefields;
 global $ignorerefresh, $changerefresh, $autorefresh;
 global $ignorerefresh, $changerefresh, $autorefresh;
 global $allowcustompages, $customsummarypages;
 global $allowcustompages, $customsummarypages;
 global $miner_font_family, $miner_font_size;
 global $miner_font_family, $miner_font_size;
-global $colouroverride, $placebuttons;
+global $colouroverride, $placebuttons, $userlist;
 #
 #
 # See API-README for more details of these variables and how
 # See API-README for more details of these variables and how
 # to configure miner.php
 # to configure miner.php
@@ -20,6 +20,9 @@ $title = 'Mine';
 # Set $readonly to false then it will check cgminer 'privileged'
 # Set $readonly to false then it will check cgminer 'privileged'
 $readonly = false;
 $readonly = false;
 #
 #
+# Set $userlist to null to allow anyone access or read API-README
+$userlist = null;
+#
 # Set $notify to false to NOT attempt to display the notify command
 # Set $notify to false to NOT attempt to display the notify command
 # Set $notify to true to attempt to display the notify command
 # Set $notify to true to attempt to display the notify command
 $notify = true;
 $notify = true;
@@ -113,16 +116,17 @@ $poolspage = array(
 			'POOL.Difficulty Rejected=Diff Rej',
 			'POOL.Difficulty Rejected=Diff Rej',
 			'POOL.Has Stratum=Stratum', 'POOL.Stratum Active=StrAct',
 			'POOL.Has Stratum=Stratum', 'POOL.Stratum Active=StrAct',
 			'POOL.Has GBT=GBT', 'STATS.Times Sent=TSent',
 			'POOL.Has GBT=GBT', 'STATS.Times Sent=TSent',
-			'STATS.Bytes Sent=BSent', 'STATS.Times Recv=TRecv',
-			'STATS.Bytes Recv=BRecv'));
+			'STATS.Bytes Sent=BSent', 'STATS.Net Bytes Sent=NSent',
+			'STATS.Times Recv=TRecv', 'STATS.Bytes Recv=BRecv',
+			'STATS.Net Bytes Recv=NRecv'));
 #
 #
 $poolssum = array(
 $poolssum = array(
  'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted',
  'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted',
 			'Rejected', 'Utility', 'Hardware Errors',
 			'Rejected', 'Utility', 'Hardware Errors',
 			'Work Utility'),
 			'Work Utility'),
  'POOL+STATS' => array('POOL.Difficulty Accepted', 'POOL.Difficulty Rejected',
  'POOL+STATS' => array('POOL.Difficulty Accepted', 'POOL.Difficulty Rejected',
-			'STATS.Times Sent', 'STATS.Bytes Sent',
-			'STATS.Times Recv', 'STATS.Bytes Recv'));
+			'STATS.Times Sent', 'STATS.Bytes Sent', 'STATS.Net Bytes Sent',
+			'STATS.Times Recv', 'STATS.Bytes Recv', 'STATS.Net Bytes Recv'));
 #
 #
 $poolsext = array(
 $poolsext = array(
  'POOL+STATS' => array(
  'POOL+STATS' => array(
@@ -130,7 +134,8 @@ $poolsext = array(
 	'group' => array('POOL.URL', 'POOL.Has Stratum', 'POOL.Stratum Active', 'POOL.Has GBT'),
 	'group' => array('POOL.URL', 'POOL.Has Stratum', 'POOL.Stratum Active', 'POOL.Has GBT'),
 	'calc' => array('POOL.Difficulty Accepted' => 'sum', 'POOL.Difficulty Rejected' => 'sum',
 	'calc' => array('POOL.Difficulty Accepted' => 'sum', 'POOL.Difficulty Rejected' => 'sum',
 			'STATS.Times Sent' => 'sum', 'STATS.Bytes Sent' => 'sum',
 			'STATS.Times Sent' => 'sum', 'STATS.Bytes Sent' => 'sum',
-			'STATS.Times Recv' => 'sum', 'STATS.Bytes Recv' => 'sum'),
+			'STATS.Net Bytes Sent' => 'sum', 'STATS.Times Recv' => 'sum',
+			'STATS.Bytes Recv' => 'sum', 'STATS.Net Bytes Recv' => 'sum'),
 	'having' => array(array('STATS.Bytes Recv', '>', 0)))
 	'having' => array(array('STATS.Bytes Recv', '>', 0)))
 );
 );
 
 
@@ -212,6 +217,10 @@ $rigerror = array();
 global $rownum;
 global $rownum;
 $rownum = 0;
 $rownum = 0;
 #
 #
+// Login
+global $ses;
+$ses = 'rutroh';
+#
 function getcss($cssname, $dom = false)
 function getcss($cssname, $dom = false)
 {
 {
  global $colourtable, $colouroverride;
  global $colourtable, $colouroverride;
@@ -239,7 +248,7 @@ function getdom($domname)
  return getcss($domname, true);
  return getcss($domname, true);
 }
 }
 #
 #
-function htmlhead($checkapi, $rig, $pg = null)
+function htmlhead($checkapi, $rig, $pg = null, $noscript = false)
 {
 {
  global $title, $miner_font_family, $miner_font_size;
  global $title, $miner_font_family, $miner_font_size;
  global $error, $readonly, $poolinputs, $here;
  global $error, $readonly, $poolinputs, $here;
@@ -285,8 +294,10 @@ td.lst { $miner_font ".getcss('td.lst')."}
 td.hi { $miner_font ".getcss('td.hi')."}
 td.hi { $miner_font ".getcss('td.hi')."}
 td.lo { $miner_font ".getcss('td.lo')."}
 td.lo { $miner_font ".getcss('td.lo')."}
 </style>
 </style>
-</head><body".getdom('body').">
-<script type='text/javascript'>
+</head><body".getdom('body').">\n";
+if ($noscript === false)
+{
+echo "<script type='text/javascript'>
 function pr(a,m){if(m!=null){if(!confirm(m+'?'))return}window.location='$here?ref=$autorefresh'+a}\n";
 function pr(a,m){if(m!=null){if(!confirm(m+'?'))return}window.location='$here?ref=$autorefresh'+a}\n";
 
 
 if ($ignorerefresh == false)
 if ($ignorerefresh == false)
@@ -300,8 +311,9 @@ function prs2(a,n,r){var v=document.getElementById('gi'+n).value;var c=a.substr(
 	if ($poolinputs === true)
 	if ($poolinputs === true)
 		echo "function cbs(s){var t=s.replace(/\\\\/g,'\\\\\\\\'); return t.replace(/,/g, '\\\\,')}\nfunction pla(r){var u=document.getElementById('purl').value;var w=document.getElementById('pwork').value;var p=document.getElementById('ppass').value;pr('&rig='+r+'&arg=addpool|'+cbs(u)+','+cbs(w)+','+cbs(p),'Add Pool '+u)}\nfunction psp(r){var p=document.getElementById('prio').value;pr('&rig='+r+'&arg=poolpriority|'+p,'Set Pool Priorities to '+p)}\n";
 		echo "function cbs(s){var t=s.replace(/\\\\/g,'\\\\\\\\'); return t.replace(/,/g, '\\\\,')}\nfunction pla(r){var u=document.getElementById('purl').value;var w=document.getElementById('pwork').value;var p=document.getElementById('ppass').value;pr('&rig='+r+'&arg=addpool|'+cbs(u)+','+cbs(w)+','+cbs(p),'Add Pool '+u)}\nfunction psp(r){var p=document.getElementById('prio').value;pr('&rig='+r+'&arg=poolpriority|'+p,'Set Pool Priorities to '+p)}\n";
  }
  }
+echo "</script>\n";
+}
 ?>
 ?>
-</script>
 <table width=100% height=100% border=0 cellpadding=0 cellspacing=0 summary='Mine'>
 <table width=100% height=100% border=0 cellpadding=0 cellspacing=0 summary='Mine'>
 <tr><td align=center valign=top>
 <tr><td align=center valign=top>
 <table border=0 cellpadding=4 cellspacing=0 summary='Mine'>
 <table border=0 cellpadding=4 cellspacing=0 summary='Mine'>
@@ -578,6 +590,14 @@ function classlastshare($when, $alldata, $warnclass, $errorclass)
  return '';
  return '';
 }
 }
 #
 #
+function endzero($num)
+{
+ $rep = preg_replace('/0*$/', '', $num);
+ if ($rep === '')
+	$rep = '0';
+ return $rep;
+}
+#
 function fmt($section, $name, $value, $when, $alldata)
 function fmt($section, $name, $value, $when, $alldata)
 {
 {
  global $dfmt, $rownum;
  global $dfmt, $rownum;
@@ -840,12 +860,16 @@ function fmt($section, $name, $value, $when, $alldata)
 	case 'total.Diff1 Work':
 	case 'total.Diff1 Work':
 	case 'STATS.Times Sent':
 	case 'STATS.Times Sent':
 	case 'STATS.Bytes Sent':
 	case 'STATS.Bytes Sent':
+	case 'STATS.Net Bytes Sent':
 	case 'STATS.Times Recv':
 	case 'STATS.Times Recv':
 	case 'STATS.Bytes Recv':
 	case 'STATS.Bytes Recv':
+	case 'STATS.Net Bytes Recv':
 	case 'total.Times Sent':
 	case 'total.Times Sent':
 	case 'total.Bytes Sent':
 	case 'total.Bytes Sent':
+	case 'total.Net Bytes Sent':
 	case 'total.Times Recv':
 	case 'total.Times Recv':
 	case 'total.Bytes Recv':
 	case 'total.Bytes Recv':
+	case 'total.Net Bytes Recv':
 		$parts = explode('.', $value, 2);
 		$parts = explode('.', $value, 2);
 		if (count($parts) == 1)
 		if (count($parts) == 1)
 			$dec = '';
 			$dec = '';
@@ -853,6 +877,23 @@ function fmt($section, $name, $value, $when, $alldata)
 			$dec = '.'.$parts[1];
 			$dec = '.'.$parts[1];
 		$ret = number_format((float)$parts[0]).$dec;
 		$ret = number_format((float)$parts[0]).$dec;
 		break;
 		break;
+	case 'STATS.Hs':
+	case 'STATS.W':
+	case 'STATS.history_time':
+	case 'STATS.Pool Wait':
+	case 'STATS.Pool Max':
+	case 'STATS.Pool Min':
+	case 'STATS.Pool Av':
+	case 'STATS.Min Diff':
+	case 'STATS.Max Diff':
+	case 'STATS.Work Diff':
+		$parts = explode('.', $value, 2);
+		if (count($parts) == 1)
+			$dec = '';
+		else
+			$dec = '.'.endzero($parts[1]);
+		$ret = number_format((float)$parts[0]).$dec;
+		break;
 	case 'GPU.Status':
 	case 'GPU.Status':
 	case 'PGA.Status':
 	case 'PGA.Status':
 	case 'DEVS.Status':
 	case 'DEVS.Status':
@@ -1507,7 +1548,6 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
 #
 #
 function refreshbuttons()
 function refreshbuttons()
 {
 {
- global $readonly;
  global $ignorerefresh, $changerefresh, $autorefresh;
  global $ignorerefresh, $changerefresh, $autorefresh;
 
 
  if ($ignorerefresh == false && $changerefresh == true)
  if ($ignorerefresh == false && $changerefresh == true)
@@ -1521,7 +1561,7 @@ function refreshbuttons()
 #
 #
 function pagebuttons($rig, $pg)
 function pagebuttons($rig, $pg)
 {
 {
- global $readonly, $rigs;
+ global $readonly, $rigs, $userlist, $ses;
  global $allowcustompages, $customsummarypages;
  global $allowcustompages, $customsummarypages;
 
 
  if ($rig === null)
  if ($rig === null)
@@ -1557,18 +1597,33 @@ function pagebuttons($rig, $pg)
  }
  }
 
 
  echo '<tr><td><table cellpadding=0 cellspacing=0 border=0><tr><td nowrap>';
  echo '<tr><td><table cellpadding=0 cellspacing=0 border=0><tr><td nowrap>';
- if ($prev !== null)
-	echo riginput($prev, 'Prev').'&nbsp;';
- echo "<input type=button value='Refresh' onclick='pr(\"$refresh\",null)'>&nbsp;";
- if ($next !== null)
-	echo riginput($next, 'Next').'&nbsp;';
- echo '&nbsp;';
- if (count($rigs) > 1)
-	echo "<input type=button value='Summary' onclick='pr(\"\",null)'>&nbsp;";
+ if ($userlist === null || isset($_SESSION[$ses]))
+ {
+	if ($prev !== null)
+		echo riginput($prev, 'Prev').'&nbsp;';
+	echo "<input type=button value='Refresh' onclick='pr(\"$refresh\",null)'>&nbsp;";
+	if ($next !== null)
+		echo riginput($next, 'Next').'&nbsp;';
+	echo '&nbsp;';
+	if (count($rigs) > 1)
+		echo "<input type=button value='Summary' onclick='pr(\"\",null)'>&nbsp;";
+ }
 
 
  if ($allowcustompages === true)
  if ($allowcustompages === true)
-	foreach ($customsummarypages as $pagename => $data)
+ {
+	if ($userlist === null || isset($_SESSION[$ses]))
+		$list = $customsummarypages;
+	else
+	{
+		if ($userlist !== null && isset($userlist['def']))
+			$list = array_flip($userlist['def']);
+		else
+			$list = array();
+	}
+
+	foreach ($list as $pagename => $data)
 		echo "<input type=button value='$pagename' onclick='pr(\"&pg=$pagename\",null)'>&nbsp;";
 		echo "<input type=button value='$pagename' onclick='pr(\"&pg=$pagename\",null)'>&nbsp;";
+ }
 
 
  echo '</td><td width=100%>&nbsp;</td><td nowrap>';
  echo '</td><td width=100%>&nbsp;</td><td nowrap>';
  if ($rig !== null && $readonly === false)
  if ($rig !== null && $readonly === false)
@@ -1580,6 +1635,12 @@ function pagebuttons($rig, $pg)
 	echo "&nbsp;<input type=button value='Quit' onclick='prc(\"quit&rig=$rig\",\"Quit CGMiner$rg\")'>";
 	echo "&nbsp;<input type=button value='Quit' onclick='prc(\"quit&rig=$rig\",\"Quit CGMiner$rg\")'>";
  }
  }
  refreshbuttons();
  refreshbuttons();
+ if (isset($_SESSION[$ses]))
+	echo "&nbsp;<input type=button value='Logout' onclick='pr(\"&logout=1\",null)'>";
+ else
+	if ($userlist !== null)
+		echo "&nbsp;<input type=button value='Login' onclick='pr(\"&login=1\",null)'>";
+
  echo "</td></tr></table></td></tr>";
  echo "</td></tr></table></td></tr>";
 }
 }
 #
 #
@@ -2015,6 +2076,8 @@ function docalc($func, $data)
 			if (strcasecmp($val, $ans) > 0)
 			if (strcasecmp($val, $ans) > 0)
 				$ans = $val;
 				$ans = $val;
 	return $ans;
 	return $ans;
+ case 'count':
+	return count($data);
  case 'any':
  case 'any':
  default:
  default:
 	return $data[0];
 	return $data[0];
@@ -2410,13 +2473,126 @@ function showcustompage($pagename)
 	pagebuttons(null, $pagename);
 	pagebuttons(null, $pagename);
 }
 }
 #
 #
+function onlylogin()
+{
+ global $here;
+
+ htmlhead(false, null, null, true);
+
+?>
+<tr height=15%><td>&nbsp;</td></tr>
+<tr><td>
+ <center>
+  <table width=384 height=368 cellpadding=0 cellspacing=0 border=0>
+   <tr><td>
+    <table width=100% height=100% border=0 align=center cellpadding=5 cellspacing=0>
+     <tr><td><form action='<?php echo $here; ?>' method=post>
+      <table width=200 border=0 align=center cellpadding=5 cellspacing=0>
+       <tr><td height=120 colspan=2>&nbsp;</td></tr>
+       <tr><td colspan=2 align=center valign=middle>
+        <h2>LOGIN</h2></td></tr>
+       <tr><td align=center valign=middle><div align=right>Username:</div></td>
+        <td height=33 align=center valign=middle>
+        <input type=text name=rut size=18></td></tr>
+       <tr><td align=center valign=middle><div align=right>Password:</div></td>
+        <td height=33 align=center valign=middle>
+        <input type=password name=roh size=18></td></tr>
+       <tr valign=top><td></td><td><input type=submit value=Login>
+        </td></tr>
+      </table></form></td></tr>
+    </table></td></tr>
+  </table></center>
+</td></tr>
+<?php
+}
+#
+function checklogin()
+{
+ global $allowcustompages, $customsummarypages;
+ global $readonly, $userlist, $ses;
+
+ $out = trim(getparam('logout', true));
+ if ($out !== null && $out !== '' && isset($_SESSION[$ses]))
+	unset($_SESSION[$ses]);
+
+ $login = trim(getparam('login', true));
+ if ($login !== null && $login !== '')
+ {
+	if (isset($_SESSION[$ses]))
+		unset($_SESSION[$ses]);
+
+	onlylogin();
+	return 'login';
+ }
+
+ if ($userlist === null)
+	return false;
+
+ $rut = trim(getparam('rut', true));
+ $roh = trim(getparam('roh', true));
+
+ if (($rut !== null && $rut !== '') || ($roh !== null && $roh !== ''))
+ {
+	if (isset($_SESSION[$ses]))
+		unset($_SESSION[$ses]);
+
+	if ($rut !== null && $rut !== '' && $roh !== null && $roh !== '')
+	{
+		if (isset($userlist['sys']) && isset($userlist['sys'][$rut])
+		&&  ($userlist['sys'][$rut] === $roh))
+		{
+			$_SESSION[$ses] = true;
+			return false;
+		}
+
+		if (isset($userlist['usr']) && isset($userlist['usr'][$rut])
+		&&  ($userlist['usr'][$rut] === $roh))
+		{
+			$_SESSION[$ses] = false;
+			$readonly = true;
+			return false;
+		}
+	}
+ }
+
+ if (isset($_SESSION[$ses]))
+ {
+	if ($_SESSION[$ses] == false)
+		$readonly = true;
+	return false;
+ }
+
+ if (isset($userlist['def']) && $allowcustompages === true)
+ {
+	// Ensure at least one exists
+	foreach ($userlist['def'] as $pg)
+		if (isset($customsummarypages[$pg]))
+			return true;
+ }
+
+ onlylogin();
+ return 'login';
+}
+#
 function display()
 function display()
 {
 {
  global $miner, $port;
  global $miner, $port;
  global $readonly, $notify, $rigs;
  global $readonly, $notify, $rigs;
  global $ignorerefresh, $autorefresh;
  global $ignorerefresh, $autorefresh;
- global $allowcustompages;
+ global $allowcustompages, $customsummarypages;
  global $placebuttons;
  global $placebuttons;
+ global $userlist, $ses;
+
+ $pagesonly = checklogin();
+
+ if ($pagesonly === 'login')
+	return;
+
+ if ($rigs == null or count($rigs) == 0)
+ {
+	otherrow("<td>No rigs defined</td>");
+	return;
+ }
 
 
  if ($ignorerefresh == false)
  if ($ignorerefresh == false)
  {
  {
@@ -2425,52 +2601,65 @@ function display()
 		$autorefresh = intval($ref);
 		$autorefresh = intval($ref);
  }
  }
 
 
- $rig = trim(getparam('rig', true));
-
- $arg = trim(getparam('arg', true));
- $preprocess = null;
- if ($arg != null and $arg != '')
+ if ($pagesonly !== true)
  {
  {
-	$num = null;
-	if ($rig != null and $rig != '')
-	{
-		if ($rig >= 0 and $rig < count($rigs))
-			$num = $rig;
-	}
-	else
-		if (count($rigs) == 0)
-			$num = 0;
+	$rig = trim(getparam('rig', true));
 
 
-	if ($num != null)
+	$arg = trim(getparam('arg', true));
+	$preprocess = null;
+	if ($arg != null and $arg != '')
 	{
 	{
-		$parts = explode(':', $rigs[$num], 3);
-		if (count($parts) >= 2)
+		if ($rig != null and $rig != '' and $rig >= 0 and $rig < count($rigs))
 		{
 		{
-			$miner = $parts[0];
-			$port = $parts[1];
+			$parts = explode(':', $rigs[$rig], 3);
+			if (count($parts) >= 2)
+			{
+				$miner = $parts[0];
+				$port = $parts[1];
 
 
-			if ($readonly !== true)
-				$preprocess = $arg;
+				if ($readonly !== true)
+					$preprocess = $arg;
+			}
 		}
 		}
 	}
 	}
  }
  }
 
 
- if ($rigs == null or count($rigs) == 0)
- {
-	otherrow("<td>No rigs defined</td>");
-	return;
- }
-
  if ($allowcustompages === true)
  if ($allowcustompages === true)
  {
  {
 	$pg = trim(getparam('pg', true));
 	$pg = trim(getparam('pg', true));
-	if ($pg != null && $pg != '')
+	if ($pagesonly === true)
+	{
+		if ($pg !== null && $pg !== '')
+		{
+			if ($userlist !== null && isset($userlist['def'])
+			&&  !in_array($pg, $userlist['def']))
+				$pg = null;
+		}
+		else
+		{
+			if ($userlist !== null && isset($userlist['def']))
+				foreach ($userlist['def'] as $pglook)
+					if (isset($customsummarypages[$pglook]))
+					{
+						$pg = $pglook;
+						break;
+					}
+		}
+	}
+
+	if ($pg !== null && $pg !== '')
 	{
 	{
 		showcustompage($pg);
 		showcustompage($pg);
 		return;
 		return;
 	}
 	}
  }
  }
 
 
+ if ($pagesonly === true)
+ {
+	onlylogin();
+	return;
+ }
+
  if (count($rigs) == 1)
  if (count($rigs) == 1)
  {
  {
 	$parts = explode(':', $rigs[0], 3);
 	$parts = explode(':', $rigs[0], 3);

File diff suppressed because it is too large
+ 486 - 203
usbutils.c


+ 65 - 11
usbutils.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2012 Andrew Smith
+ * Copyright 2012-2013 Andrew Smith
  *
  *
  * This program is free software; you can redistribute it and/or modify it
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
  * under the terms of the GNU General Public License as published by the Free
@@ -12,6 +12,29 @@
 
 
 #include <libusb.h>
 #include <libusb.h>
 
 
+// for 0x0403/0x6014 FT232H (and possibly others?)
+#define FTDI_TYPE_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT)
+
+#define FTDI_REQUEST_RESET ((uint8_t)0)
+#define FTDI_REQUEST_MODEM ((uint8_t)1)
+#define FTDI_REQUEST_FLOW ((uint8_t)2)
+#define FTDI_REQUEST_BAUD ((uint8_t)3)
+#define FTDI_REQUEST_DATA ((uint8_t)4)
+
+#define FTDI_VALUE_RESET 0
+#define FTDI_VALUE_PURGE_RX 1
+#define FTDI_VALUE_PURGE_TX 2
+
+// baud with a 0 divisor is 120,000,000/10
+//#define FTDI_VALUE_BAUD (0)
+//#define FTDI_INDEX_BAUD (0)
+#define FTDI_VALUE_BAUD 0xc068
+#define FTDI_INDEX_BAUD 0x0200
+
+#define FTDI_VALUE_DATA 0
+#define FTDI_VALUE_FLOW 0
+#define FTDI_VALUE_MODEM 0x0303
+
 // Use the device defined timeout
 // Use the device defined timeout
 #define DEVTIMEOUT 0
 #define DEVTIMEOUT 0
 
 
@@ -32,6 +55,7 @@ struct usb_find_devices {
 	const char *name;
 	const char *name;
 	uint16_t idVendor;
 	uint16_t idVendor;
 	uint16_t idProduct;
 	uint16_t idProduct;
+	int kernel;
 	int config;
 	int config;
 	int interface;
 	int interface;
 	unsigned int timeout;
 	unsigned int timeout;
@@ -44,8 +68,6 @@ struct cg_usb_device {
 	libusb_device_handle *handle;
 	libusb_device_handle *handle;
 	pthread_mutex_t *mutex;
 	pthread_mutex_t *mutex;
 	struct libusb_device_descriptor *descriptor;
 	struct libusb_device_descriptor *descriptor;
-	uint8_t bus_number;
-	uint8_t device_address;
 	uint16_t usbver;
 	uint16_t usbver;
 	int speed;
 	int speed;
 	char *prod_string;
 	char *prod_string;
@@ -55,8 +77,18 @@ struct cg_usb_device {
 	unsigned char interfaceVersion;	// ??
 	unsigned char interfaceVersion;	// ??
 };
 };
 
 
+struct cg_usb_info {
+	uint8_t bus_number;
+	uint8_t device_address;
+	int usbstat;
+	bool nodev;
+	int nodev_count;
+	struct timeval last_nodev;
+};
+
 enum usb_cmds {
 enum usb_cmds {
-	C_PING = 0,
+	C_REJECTED = 0,
+	C_PING,
 	C_CLEAR,
 	C_CLEAR,
 	C_REQUESTVERSION,
 	C_REQUESTVERSION,
 	C_GETVERSION,
 	C_GETVERSION,
@@ -78,32 +110,48 @@ enum usb_cmds {
 	C_SENDWORKSTATUS,
 	C_SENDWORKSTATUS,
 	C_REQUESTWORKSTATUS,
 	C_REQUESTWORKSTATUS,
 	C_GETWORKSTATUS,
 	C_GETWORKSTATUS,
+	C_REQUESTIDENTIFY,
+	C_GETIDENTIFY,
+	C_REQUESTFLASH,
+	C_REQUESTSENDWORK,
+	C_REQUESTSENDWORKSTATUS,
+	C_RESET,
+	C_SETBAUD,
+	C_SETDATA,
+	C_SETFLOW,
+	C_SETMODEM,
+	C_PURGERX,
+	C_PURGETX,
 	C_MAX
 	C_MAX
 };
 };
 
 
-struct device_api;
+struct device_drv;
 struct cgpu_info;
 struct cgpu_info;
 
 
 void usb_uninit(struct cgpu_info *cgpu);
 void usb_uninit(struct cgpu_info *cgpu);
 bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found);
 bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found);
-void usb_detect(struct device_api *api, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *));
+void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *));
 struct api_data *api_usb_stats(int *count);
 struct api_data *api_usb_stats(int *count);
 void update_usb_stats(struct cgpu_info *cgpu);
 void update_usb_stats(struct cgpu_info *cgpu);
-int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds);
+int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds, bool ftdi);
 int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds);
 int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds);
+int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout, enum usb_cmds cmd);
 void usb_cleanup();
 void usb_cleanup();
 
 
 #define usb_read(cgpu, buf, bufsiz, read, cmd) \
 #define usb_read(cgpu, buf, bufsiz, read, cmd) \
-	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, -1, cmd)
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, -1, cmd, false)
+
+#define usb_read_nl(cgpu, buf, bufsiz, read, cmd) \
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, '\n', cmd, false)
 
 
 #define usb_read_ep(cgpu, ep, buf, bufsiz, read, cmd) \
 #define usb_read_ep(cgpu, ep, buf, bufsiz, read, cmd) \
-	_usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, -1, cmd)
+	_usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, -1, cmd, false)
 
 
 #define usb_read_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \
 #define usb_read_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \
-	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, -1, cmd)
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, -1, cmd, false)
 
 
 #define usb_read_ep_timeout(cgpu, ep, buf, bufsiz, read, timeout, cmd) \
 #define usb_read_ep_timeout(cgpu, ep, buf, bufsiz, read, timeout, cmd) \
-	_usb_read(cgpu, ep, buf, bufsiz, read, timeout, -1, cmd)
+	_usb_read(cgpu, ep, buf, bufsiz, read, timeout, -1, cmd, false)
 
 
 #define usb_write(cgpu, buf, bufsiz, wrote, cmd) \
 #define usb_write(cgpu, buf, bufsiz, wrote, cmd) \
 	_usb_write(cgpu, DEFAULT_EP_OUT, buf, bufsiz, wrote, DEVTIMEOUT, cmd)
 	_usb_write(cgpu, DEFAULT_EP_OUT, buf, bufsiz, wrote, DEVTIMEOUT, cmd)
@@ -117,4 +165,10 @@ void usb_cleanup();
 #define usb_write_ep_timeout(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) \
 #define usb_write_ep_timeout(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) \
 	_usb_write(cgpu, ep, buf, bufsiz, wrote, timeout, cmd)
 	_usb_write(cgpu, ep, buf, bufsiz, wrote, timeout, cmd)
 
 
+#define usb_ftdi_read_nl(cgpu, buf, bufsiz, read, cmd) \
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, '\n', cmd, true)
+
+#define usb_transfer(cgpu, typ, req, val, idx, cmd) \
+	_usb_transfer(cgpu, typ, req, val, idx, DEVTIMEOUT, cmd)
+
 #endif
 #endif

+ 30 - 4
util.c

@@ -261,6 +261,29 @@ static void set_nettime(void)
 	wr_unlock(&netacc_lock);
 	wr_unlock(&netacc_lock);
 }
 }
 
 
+static int curl_debug_cb(__maybe_unused CURL *handle, curl_infotype type,
+			 __maybe_unused char *data, size_t size, void *userdata)
+{
+	struct pool *pool = (struct pool *)userdata;
+
+	switch(type) {
+		case CURLINFO_HEADER_IN:
+		case CURLINFO_DATA_IN:
+		case CURLINFO_SSL_DATA_IN:
+			pool->cgminer_pool_stats.net_bytes_received += size;
+			break;
+		case CURLINFO_HEADER_OUT:
+		case CURLINFO_DATA_OUT:
+		case CURLINFO_SSL_DATA_OUT:
+			pool->cgminer_pool_stats.net_bytes_sent += size;
+			break;
+		case CURLINFO_TEXT:
+		default:
+			break;
+	}
+	return 0;
+}
+
 json_t *json_rpc_call(CURL *curl, const char *url,
 json_t *json_rpc_call(CURL *curl, const char *url,
 		      const char *userpass, const char *rpc_req,
 		      const char *userpass, const char *rpc_req,
 		      bool probe, bool longpoll, int *rolltime,
 		      bool probe, bool longpoll, int *rolltime,
@@ -287,10 +310,11 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 		probing = !pool->probed;
 		probing = !pool->probed;
 	curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
 	curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
 
 
-#if 0 /* Disable curl debugging since it spews to stderr */
-	if (opt_protocol)
-		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
-#endif
+	// CURLOPT_VERBOSE won't write to stderr if we use CURLOPT_DEBUGFUNCTION
+	curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_debug_cb);
+	curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)pool);
+	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+
 	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
 	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
 	curl_easy_setopt(curl, CURLOPT_URL, url);
 	curl_easy_setopt(curl, CURLOPT_URL, url);
 	curl_easy_setopt(curl, CURLOPT_ENCODING, "");
 	curl_easy_setopt(curl, CURLOPT_ENCODING, "");
@@ -912,6 +936,7 @@ static bool __stratum_send(struct pool *pool, char *s, ssize_t len)
 
 
 	pool->cgminer_pool_stats.times_sent++;
 	pool->cgminer_pool_stats.times_sent++;
 	pool->cgminer_pool_stats.bytes_sent += ssent;
 	pool->cgminer_pool_stats.bytes_sent += ssent;
+	pool->cgminer_pool_stats.net_bytes_sent += ssent;
 	return true;
 	return true;
 }
 }
 
 
@@ -1041,6 +1066,7 @@ char *recv_line(struct pool *pool)
 
 
 	pool->cgminer_pool_stats.times_received++;
 	pool->cgminer_pool_stats.times_received++;
 	pool->cgminer_pool_stats.bytes_received += len;
 	pool->cgminer_pool_stats.bytes_received += len;
+	pool->cgminer_pool_stats.net_bytes_received += len;
 out:
 out:
 	if (!sret)
 	if (!sret)
 		clear_sock(pool);
 		clear_sock(pool);

Some files were not shown because too many files changed in this diff