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:
                                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
 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:
 
 
-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:
  '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:
  $notify = true;
 
@@ -1302,9 +1357,12 @@ You can only see fields listed in 'group' and 'calc'
 
 A 'calc' is formatted as: 'Field' => 'function'
 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
 'lo' is the first 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
 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
 	{
+		StringBuffer sb = new StringBuffer();
 		char buf[] = new char[MAXRECEIVESIZE];
 		int len = 0;
 
@@ -89,7 +90,15 @@ System.out.println("Attempting to send '"+cmd+"' to "+ip.getHostAddress()+":"+po
 			ps.flush();
 
 			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();
 		}
@@ -100,7 +109,7 @@ System.out.println("Attempting to send '"+cmd+"' to "+ip.getHostAddress()+":"+po
 			return;
 		}
 
-		String result = new String(buf, 0, len);
+		String result = sb.toString();
 
 		System.out.println("Answer='"+result+"'");
 

+ 14 - 11
FPGA-README

@@ -2,15 +2,8 @@
 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.
 
@@ -39,7 +32,17 @@ problems:
 
  --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
 
 
-Bitforce (BFL)
+BitForce (BFL)
 --------------
 
 --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
 endif
 
+if NEED_USBUTILS_C
+cgminer_SOURCES += usbutils.c
+endif
+
 if HAS_BITFORCE
 cgminer_SOURCES += driver-bitforce.c
 endif
@@ -91,7 +95,7 @@ cgminer_SOURCES += driver-icarus.c
 endif
 
 if HAS_MODMINER
-cgminer_SOURCES += driver-modminer.c usbutils.c
+cgminer_SOURCES += driver-modminer.c
 bitstreamsdir = $(bindir)/bitstreams
 dist_bitstreams_DATA = bitstreams/*
 endif

+ 11 - 9
README

@@ -220,15 +220,16 @@ SCRYPT only options:
 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
 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
@@ -237,13 +238,14 @@ device depending on the version of udev being used.
 
 On linux <arg> is usually of the format /dev/ttyUSBn
 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.
 To force the code to only attempt detection with a specific driver,
 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
 
@@ -455,7 +457,7 @@ The block display shows:
 Block: 0074c5e482e34a506d2a051a...  Started: [17:17:22]  Best share: 2.71K
 
 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.
 
 

+ 1 - 1
adl.c

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

+ 153 - 62
api.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2011-2012 Andrew Smith
+ * Copyright 2011-2013 Andrew Smith
  * Copyright 2011-2012 Con Kolivas
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -133,7 +133,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 
-static const char *APIVERSION = "1.23";
+static const char *APIVERSION = "1.24";
 static const char *DEAD = "Dead";
 #if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
 static const char *SICK = "Sick";
@@ -387,6 +387,12 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_PGASETERR 93
 #endif
 
+#define MSG_ZERMIS 94
+#define MSG_ZERINV 95
+#define MSG_ZERSUM 96
+#define MSG_ZERNOSUM 97
+#define MSG_USBNODEV 98
+
 enum code_severity {
 	SEVERITY_ERR,
 	SEVERITY_WARN,
@@ -558,6 +564,13 @@ struct CODES {
  { SEVERITY_INFO,  MSG_PGAHELP, PARAM_BOTH,	"PGA %d set help: %s" },
  { SEVERITY_SUCC,  MSG_PGASETOK, PARAM_BOTH,	"PGA %d set OK" },
  { 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
  { SEVERITY_FAIL, 0, 0, NULL }
 };
@@ -599,22 +612,6 @@ struct APIGROUPS {
 static struct IP4ACCESS *ipaccess = NULL;
 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 {
 	size_t siz;
 	char *ptr;
@@ -1151,24 +1148,26 @@ static int numpgas()
 	int count = 0;
 	int i;
 
+	mutex_lock(&devices_lock);
 	for (i = 0; i < total_devices; i++) {
 #ifdef USE_BITFORCE
-		if (devices[i]->api == &bitforce_api)
+		if (devices[i]->drv->drv_id == DRIVER_BITFORCE)
 			count++;
 #endif
 #ifdef USE_ICARUS
-		if (devices[i]->api == &icarus_api)
+		if (devices[i]->drv->drv_id == DRIVER_ICARUS)
 			count++;
 #endif
 #ifdef USE_ZTEX
-		if (devices[i]->api == &ztex_api)
+		if (devices[i]->drv->drv_id == DRIVER_ZTEX)
 			count++;
 #endif
 #ifdef USE_MODMINER
-		if (devices[i]->api == &modminer_api)
+		if (devices[i]->drv->drv_id == DRIVER_MODMINER)
 			count++;
 #endif
 	}
+	mutex_unlock(&devices_lock);
 	return count;
 }
 
@@ -1177,27 +1176,35 @@ static int pgadevice(int pgaid)
 	int count = 0;
 	int i;
 
+	mutex_lock(&devices_lock);
 	for (i = 0; i < total_devices; i++) {
 #ifdef USE_BITFORCE
-		if (devices[i]->api == &bitforce_api)
+		if (devices[i]->drv->drv_id == DRIVER_BITFORCE)
 			count++;
 #endif
 #ifdef USE_ICARUS
-		if (devices[i]->api == &icarus_api)
+		if (devices[i]->drv->drv_id == DRIVER_ICARUS)
 			count++;
 #endif
 #ifdef USE_ZTEX
-		if (devices[i]->api == &ztex_api)
+		if (devices[i]->drv->drv_id == DRIVER_ZTEX)
 			count++;
 #endif
 #ifdef USE_MODMINER
-		if (devices[i]->api == &modminer_api)
+		if (devices[i]->drv->drv_id == DRIVER_MODMINER)
 			count++;
 #endif
 		if (count == (pgaid + 1))
-			return i;
+			goto foundit;
 	}
+
+	mutex_unlock(&devices_lock);
 	return -1;
+
+foundit:
+
+	mutex_unlock(&devices_lock);
+	return i;
 }
 #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
 			return;
 
-		struct cgpu_info *cgpu = devices[dev];
+		struct cgpu_info *cgpu = get_devices(dev);
 		double frequency = 0;
 		float temp = cgpu->temp;
 
 #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);
 #endif
 #ifdef USE_MODMINER
-		if (cgpu->api == &modminer_api)
+		if (cgpu->drv->drv_id == DRIVER_MODMINER)
 			frequency = cgpu->clock;
 #endif
 
@@ -1553,7 +1560,7 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom
 		status = (char *)status2str(cgpu->status);
 
 		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_string(root, "Enabled", enabled, 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 Rejected", &(cgpu->diff_rejected), 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);
 		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)
 {
+	struct cgpu_info *cgpu;
 	int numpga = numpgas();
 	struct thr_info *thr;
 	int pga;
@@ -1775,7 +1786,10 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 		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) {
 		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
 
+#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++) {
-		pga = thr_info[i].cgpu->device_id;
+		thr = get_thread(i);
+		pga = thr->cgpu->cgminer_id;
 		if (pga == dev) {
-			thr = &thr_info[i];
 			cgpu->deven = DEV_ENABLED;
+			applog(LOG_DEBUG, "API: pushing ping (%d) to thread %d", ping, thr->id);
 			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)
 {
+	struct cgpu_info *cgpu;
 	int numpga = numpgas();
 	int id;
 
@@ -1828,7 +1851,10 @@ static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha
 		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) {
 		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)
 {
+	struct cgpu_info *cgpu;
+	struct device_drv *drv;
 	int numpga = numpgas();
 	int id;
 
@@ -1867,13 +1895,13 @@ static void pgaidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, ch
 		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);
 	else {
-		api->identify_device(cgpu);
+		drv->identify_device(cgpu);
 		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
 			root = api_add_const(root, "Stratum URL", BLANK, 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));
 		io_add(io_data, buf);
@@ -2081,23 +2110,25 @@ static void gpuenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 		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) {
 		message(io_data, MSG_ALRENA, id, NULL, isjson);
 		return;
 	}
 
 	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) {
-			thr = &thr_info[i];
 			if (thr->cgpu->status != LIFE_WELL) {
 				message(io_data, MSG_GPUMRE, id, NULL, isjson);
 				return;
 			}
-
 			gpus[id].deven = DEV_ENABLED;
+			applog(LOG_DEBUG, "API: pushing ping (%d) to thread %d", ping, thr->id);
 			tq_push(thr->q, &ping);
-
 		}
 	}
 
@@ -2124,6 +2155,9 @@ static void gpudisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha
 		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) {
 		message(io_data, MSG_ALRDIS, id, NULL, isjson);
 		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 '*'
 	// Simplifies future external support for identifying new counters
 	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_time(root, "Last Well", &(cgpu->device_last_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)
 {
+	struct cgpu_info *cgpu;
 	bool io_open = false;
 	int i;
 
@@ -2787,8 +2822,10 @@ static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe
 	if (isjson)
 		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)
 		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);
 
 	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_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_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, "Model", cgpu->name ? : 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, "Times Recv", &(pool_stats->times_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)
@@ -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)
 {
+	struct cgpu_info *cgpu;
 	bool io_open = false;
 	struct api_data *extra;
 	char id[20];
@@ -2920,15 +2960,15 @@ static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m
 
 	i = 0;
 	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
 				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);
 		}
 	}
@@ -3120,7 +3160,7 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may
 {
 	struct api_data *root = NULL;
 
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 	char buf[TMPBUFSIZ];
 	bool io_open = false;
 	int count = 0;
@@ -3133,7 +3173,7 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may
 		return;
 	}
 
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 
 	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
 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];
 	int numpga = numpgas();
 
@@ -3193,17 +3235,17 @@ static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe
 		return;
 	}
 
-	struct cgpu_info *cgpu = devices[dev];
-	struct device_api *api = cgpu->api;
+	cgpu = get_devices(dev);
+	drv = cgpu->drv;
 
 	char *set = strchr(opt, ',');
 	if (set)
 		*(set++) = '\0';
 
-	if (!api->set_device)
+	if (!drv->set_device)
 		message(io_data, MSG_PGANOSET, id, NULL, isjson);
 	else {
-		char *ret = api->set_device(cgpu, opt, set, buf);
+		char *ret = drv->set_device(cgpu, opt, set, buf);
 		if (ret) {
 			if (strcasecmp(opt, "help") == 0)
 				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
 
+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);
 
 struct CMDS {
@@ -3274,6 +3364,7 @@ struct CMDS {
 #ifdef HAVE_AN_FPGA
 	{ "pgaset",		pgaset,		true },
 #endif
+	{ "zero",		dozero,		true },
 	{ NULL,			NULL,		false }
 };
 
@@ -3701,7 +3792,7 @@ void api(int api_thr_id)
 
 	/* This should be done before curl in needed
 	 * 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);
 	if (sock == INVSOCK) {
@@ -3747,7 +3838,7 @@ void api(int api_thr_id)
 				break;
 			else {
 				applog(LOG_WARNING, "API bind to port %d failed - trying again in 30sec", port);
-				sleep(30);
+				nmsleep(30000);
 			}
 		} else
 			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_USBUTILS_C], [test x$bitforce$modminer != xnono])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue])
 AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue])
@@ -321,7 +322,7 @@ fi
 
 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)])],
 		[libudev=$withval],
 		[libudev=auto]
@@ -343,7 +344,7 @@ AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno])
 
 PKG_PROG_PKG_CONFIG()
 
-if test "x$ztex$modminer" != xnono; then
+if test "x$ztex$modminer$bitforce" != xnonono; then
   case $target in
     *-*-freebsd*)
       LIBUSB_LIBS="-lusb"
@@ -508,7 +509,7 @@ else
 	echo "  Ztex.FPGAs...........: Disabled"
 fi
 
-if test "x$bitforce" != xno; then
+if test "x$icarus" != xno; then
 	echo "  libudev.detection....: $libudev"
 fi
 

+ 359 - 321
driver-bitforce.c

@@ -1,4 +1,5 @@
 /*
+ * Copyright 2012-2013 Andrew Smith
  * Copyright 2012 Luke Dashjr
  * Copyright 2012 Con Kolivas
  *
@@ -19,34 +20,35 @@
 #include "config.h"
 
 #ifdef WIN32
-
 #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 */
 
 #include "compat.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_TIMEOUT_S 7
@@ -62,62 +64,197 @@ enum {
 #define KNAME_WORK  "full work"
 #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;
+	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!
-	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
 	 * fails */
 	if (opt_bfl_noncerange) {
@@ -129,108 +266,39 @@ static bool bitforce_detect_one(const char *devpath)
 		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);
 
-	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)
 {
-	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)
@@ -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)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
-	int fdDev = BFopen(bitforce->device_path);
 	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);
 	get_datestamp(bitforce->init, &now);
 
 	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)
 {
-	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
 	 * minimise the chance of interleaved results */
@@ -353,19 +333,22 @@ static void bitforce_flash_led(struct cgpu_info *bitforce)
 		return;
 
 	/* 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))
 		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 */
 	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);
 
 	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)
 {
-	int fdDev = bitforce->device_fd;
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	int err, amount;
 	char *s;
 
-	if (!fdDev)
+	// Device is gone
+	if (bitforce->usbinfo.nodev)
 		return false;
 
 	/* 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))
 		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++;
 		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);
 
 		/* 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) {
 			bitforce->temp = temp;
 			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;
 				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
 		 * our responses are out of sync and flush the buffer to
 		 * 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);
 		/* Count throttling episodes as hardware errors */
 		bitforce->hw_errors++;
-		bitforce_clear_buffer(bitforce);
+		bitforce_initialise(bitforce, true);
 		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)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
-	int fdDev = bitforce->device_fd;
 	unsigned char ob[70];
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	int err, amount;
 	char *s;
+	char *cmd;
+	int len;
 
-	if (!fdDev)
-		return false;
 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);
-	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);
 		nmsleep(WORK_CHECK_INTERVAL_MS);
 		goto re_send;
-	} else if (unlikely(strncasecmp(pdevbuf, "OK", 2))) {
+	} else if (unlikely(strncasecmp(buf, "OK", 2))) {
 		mutex_unlock(&bitforce->device_mutex);
 		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->sleep_ms *= 5;
 			bitforce->kname = KNAME_WORK;
 			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;
 	}
 
@@ -479,7 +495,7 @@ re_send:
 	if (!bitforce->nonce_range) {
 		sprintf((char *)ob + 8 + 32 + 12, ">>>>>>>>");
 		work->blk.nonce = bitforce->nonces = 0xffffffff;
-		BFwrite(fdDev, ob, 60);
+		len = 60;
 	} else {
 		uint32_t *nonce;
 
@@ -491,26 +507,41 @@ re_send:
 		*nonce = htobe32(work->blk.nonce + bitforce->nonces);
 		work->blk.nonce += bitforce->nonces + 1;
 		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);
 
 	if (opt_debug) {
 		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);
 	}
 
-	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;
 	}
 
-	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;
 	}
 
@@ -521,53 +552,52 @@ re_send:
 static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
-	int fdDev = bitforce->device_fd;
 	unsigned int delay_time_ms;
 	struct timeval elapsed;
 	struct timeval now;
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	int amount;
 	char *pnoncebuf;
 	uint32_t nonce;
 
-	if (!fdDev)
-		return -1;
-
 	while (1) {
 		if (unlikely(thr->work_restart))
 			return 0;
 
 		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);
 
 		gettimeofday(&now, NULL);
 		timersub(&now, &bitforce->work_start_tv, &elapsed);
 
 		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);
 			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;
 
 		/* 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);
 		bitforce->wait_ms += delay_time_ms;
 	}
 
 	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);
 		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;
-	} 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
 		 * OS timer delays being variably reliable. wait_ms will
 		 * 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)
-			  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 */
 		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);
 	}
 
-	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 */
-	else if (!strncasecmp(pdevbuf, "I", 1))
+	else if (!strncasecmp(buf, BITFORCE_IDLE, BITFORCE_IDLE_MATCH))
 		return 0;	/* Device idle */
-	else if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) {
+	else if (strncasecmp(buf, BITFORCE_NONCE, BITFORCE_NONCE_LEN)) {
 		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;
 	}
 
-	pnoncebuf = &pdevbuf[12];
+	pnoncebuf = &buf[12];
 
 	while (1) {
 		hex2bin((void*)&nonce, pnoncebuf, 4);
@@ -612,7 +647,8 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 #endif
 		if (unlikely(bitforce->nonce_range && (nonce >= work->blk.nonce ||
 			(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;
 				work->blk.nonce = 0xffffffff;
 				bitforce->sleep_ms *= 5;
@@ -628,19 +664,16 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 	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)
 {
 	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)
@@ -649,6 +682,10 @@ static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_
 	bool send_ret;
 	int64_t ret;
 
+	// Device is gone
+	if (bitforce->usbinfo.nodev)
+		return -1;
+
 	send_ret = bitforce_send_work(thr, work);
 
 	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) {
 		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);
 		bitforce->hw_errors++;
 		/* empty read buffer */
-		bitforce_clear_buffer(bitforce);
+		bitforce_initialise(bitforce, true);
 	}
 	return ret;
 }
@@ -692,7 +729,8 @@ static bool bitforce_thread_init(struct thr_info *thr)
 	/* Pause each new thread at least 100ms between initialising
 	 * so the devices aren't making calls all at the same time. */
 	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);
 
 	return true;
@@ -712,12 +750,12 @@ static struct api_data *bitforce_api_stats(struct cgpu_info *cgpu)
 	return root;
 }
 
-struct device_api bitforce_api = {
-	.dname = "bitforce",
+struct device_drv bitforce_drv = {
+	.drv_id = DRIVER_BITFORCE,
+	.dname = "BitForce",
 	.name = "BFL",
-	.api_detect = bitforce_detect,
+	.drv_detect = bitforce_detect,
 	.get_api_stats = bitforce_api_stats,
-	.reinit_device = bitforce_init,
 	.get_statline_before = get_bitforce_statline_before,
 	.get_stats = bitforce_get_stats,
 	.identify_device = bitforce_identify,

+ 5 - 4
driver-cpu.c

@@ -758,7 +758,7 @@ static void cpu_detect()
 		struct cgpu_info *cgpu;
 
 		cgpu = &cpus[i];
-		cgpu->api = &cpu_api;
+		cgpu->drv = &cpu_drv;
 		cgpu->deven = DEV_ENABLED;
 		cgpu->threads = 1;
 		cgpu->kname = algo_names[opt_algo];
@@ -768,7 +768,7 @@ static void cpu_detect()
 
 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)
@@ -843,10 +843,11 @@ CPUSearch:
 	return last_nonce - first_nonce + 1;
 }
 
-struct device_api cpu_api = {
+struct device_drv cpu_drv = {
+	.drv_id = DRIVER_CPU,
 	.dname = "cpu",
 	.name = "CPU",
-	.api_detect = cpu_detect,
+	.drv_detect = cpu_detect,
 	.reinit_device = reinit_cpu_device,
 	.thread_prepare = cpu_thread_prepare,
 	.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 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 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;
 
-struct device_api icarus_api;
+struct device_drv icarus_drv;
 
 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! */
 	struct cgpu_info *icarus;
 	icarus = calloc(1, sizeof(struct cgpu_info));
-	icarus->api = &icarus_api;
+	icarus->drv = &icarus_drv;
 	icarus->device_path = strdup(devpath);
 	icarus->device_fd = -1;
 	icarus->threads = 1;
@@ -609,7 +609,7 @@ static bool icarus_detect_one(const char *devpath)
 
 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)
@@ -668,7 +668,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	icarus = thr->cgpu;
 	if (icarus->device_fd == -1)
 		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);
 
 			// 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));
 	if (ret) {
 		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);
 		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);
 	if (ret == ICA_GETS_ERROR) {
 		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);
 		return 0;
 	}
@@ -900,10 +900,11 @@ static void icarus_shutdown(struct thr_info *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",
-	.api_detect = icarus_detect,
+	.drv_detect = icarus_detect,
 	.get_api_stats = icarus_api_stats,
 	.thread_prepare = icarus_prepare,
 	.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
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -87,7 +87,7 @@
 // Limit when reducing shares_to_good
 #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
 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
 	err = usb_write(modminer, (char *)NOOP, sizeof(NOOP)-1, &amount, C_PING);
 	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
 	while ((err = usb_read(modminer, buf, sizeof(buf)-1, &amount, C_CLEAR)) == 0 && amount > 0)
 		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",
-		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)
@@ -121,42 +121,57 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 
 	struct cgpu_info *modminer = NULL;
 	modminer = calloc(1, sizeof(*modminer));
-	modminer->api = &modminer_api;
+	modminer->drv = &modminer_drv;
 	modminer->modminer_mutex = calloc(1, sizeof(*(modminer->modminer_mutex)));
 	mutex_init(modminer->modminer_mutex);
 	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;
+	}
+
+	sprintf(devpath, "%d:%d",
+			(int)(modminer->usbinfo.bus_number),
+			(int)(modminer->usbinfo.device_address));
 
 	do_ping(modminer);
 
 	if ((err = usb_write(modminer, MODMINER_GET_VERSION, 1, &amount, C_REQUESTVERSION)) < 0 || amount != 1) {
-		applog(LOG_ERR, "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;
 	}
 
 	if ((err = usb_read(modminer, buf, sizeof(buf)-1, &amount, C_GETVERSION)) < 0 || amount < 1) {
 		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
-			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;
 	}
 	buf[amount] = '\0';
 	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)) {
-		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;
 	}
 
 	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;
 	}
 
@@ -164,16 +179,19 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 	// can detect with modminer->cgusb->serial ?
 
 	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;
 	}
 
 	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;
 	}
 
-	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;
 
@@ -182,19 +200,21 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 	for (i = 0; i < buf[0]; i++) {
 		struct cgpu_info *tmp = calloc(1, sizeof(*tmp));
 
-		tmp->api = modminer->api;
+		tmp->drv = copy_drv(modminer->drv);
 		tmp->name = devname;
 
 		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);
 
 		tmp->device_path = strdup(devpath);
 		tmp->usbdev = modminer->usbdev;
+		tmp->usbinfo.bus_number = modminer->usbinfo.bus_number;
+		tmp->usbinfo.device_address = modminer->usbinfo.device_address;
 		// Only the first copy gets the already used stats
 		if (!added)
-			tmp->usbstat = modminer->usbstat;
+			tmp->usbinfo.usbstat = modminer->usbinfo.usbstat;
 		tmp->fpgaid = (char)i;
 		tmp->modminer_mutex = modminer->modminer_mutex;
 		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)) {
 			free(tmp->device_path);
+			if (tmp->drv->copy)
+				free(tmp->drv);
 			free(tmp);
 			goto unshin;
 		}
@@ -211,6 +233,9 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 		added = true;
 	}
 
+	if (modminer->drv->copy)
+		free(modminer->drv);
+
 	free(modminer);
 
 	return true;
@@ -223,6 +248,9 @@ shin:
 	if (!added)
 		free(modminer->modminer_mutex);
 
+	if (modminer->drv->copy)
+		free(modminer->drv);
+
 	free(modminer);
 
 	if (added)
@@ -233,7 +261,7 @@ shin:
 
 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)
@@ -242,13 +270,13 @@ static bool get_expect(struct cgpu_info *modminer, FILE *f, char c)
 
 	if (fread(&buf, 1, 1, f) != 1) {
 		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;
 	}
 
 	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;
 	}
 
@@ -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) {
 		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;
 	}
 
@@ -270,13 +298,13 @@ static bool get_info(struct cgpu_info *modminer, FILE *f, char *buf, int bufsiz,
 
 	if (len >= bufsiz) {
 		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;
 	}
 
 	if (fread(buf, len, 1, f) != 1) {
 		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;
 	}
 
@@ -302,7 +330,7 @@ static bool get_status_timeout(struct cgpu_info *modminer, char *msg, unsigned i
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
@@ -311,7 +339,7 @@ static bool get_status_timeout(struct cgpu_info *modminer, char *msg, unsigned i
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
@@ -343,7 +371,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
@@ -352,7 +380,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
@@ -361,7 +389,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 
 		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],
 			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);
 
 		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;
 	}
@@ -384,7 +412,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		goto undame;
 
 	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);
 
@@ -399,7 +427,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 
 		applog(LOG_ERR, "%s%u: Bad usercode in bitstream file",
-			modminer->api->name, modminer->device_id);
+			modminer->drv->name, modminer->device_id);
 
 		goto dame;
 	}
@@ -408,7 +436,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
@@ -446,7 +474,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
@@ -460,7 +488,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		*ptr = '\0';
 
 	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[1] = fpgaid;
@@ -473,7 +501,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
@@ -492,7 +520,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 			mutex_unlock(modminer->modminer_mutex);
 
 			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;
 		}
@@ -507,7 +535,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 
 				if (opt_debug)
 					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);
 
 				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);
 
 				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;
 			}
@@ -532,7 +560,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		if (upto >= nextmsg) {
 			applog(LOG_WARNING,
 				"%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;
 		}
@@ -542,7 +570,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
 		goto undame;
 
 	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
 	nmsleep(666);
@@ -567,8 +595,6 @@ static bool modminer_fpga_prepare(struct thr_info *thr)
 
 	struct modminer_fpga_state *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->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
  */
+static const char *clocknodev = "clock failed - no device";
 static const char *clockoldwork = "clock already changed for this work";
 static const char *clocktoolow = "clock too low";
 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];
 	int err, amount;
 
+	// Device is gone
+	if (modminer->usbinfo.nodev)
+		return clocknodev;
+
 	// Only do once if multiple shares per work or multiple reasons
 	if (!state->new_work && !force)
 		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);
 
 		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;
 	}
@@ -662,7 +693,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
@@ -670,7 +701,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te
 	mutex_unlock(modminer->modminer_mutex);
 
 	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 " : ""),
 			modminer->clock);
 
@@ -691,7 +722,7 @@ static bool modminer_fpga_init(struct thr_info *thr)
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
@@ -700,14 +731,14 @@ static bool modminer_fpga_init(struct thr_info *thr)
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
 	}
 
 	if (memcmp(buf, BISTREAM_USER_ID, 4)) {
 		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))
 			return false;
@@ -717,7 +748,7 @@ static bool modminer_fpga_init(struct thr_info *thr)
 		mutex_unlock(modminer->modminer_mutex);
 
 		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;
@@ -740,24 +771,19 @@ static void get_modminer_statline_before(char *buf, struct cgpu_info *modminer)
 	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 modminer_fpga_state *state = thr->cgpu_data;
 	int err, amount;
+	char cmd[48];
 	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)
 		gettimeofday(&state->first_work, NULL);
 
@@ -766,20 +792,16 @@ static bool modminer_start_work(struct thr_info *thr)
 
 	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);
 
 		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;
 	}
 
 	gettimeofday(&state->tv_workstart, NULL);
-	state->hashes = 0;
 
 	sta = get_status(modminer, "start work", C_SENDWORKSTATUS);
 
@@ -799,6 +821,10 @@ static void check_temperature(struct thr_info *thr)
 	int tbytes, tamount;
 	int amount;
 
+	// Device is gone
+	if (modminer->usbinfo.nodev)
+		return;
+
 	if (state->one_byte_temp) {
 		cmd[0] = MODMINER_TEMP1;
 		tbytes = 1;
@@ -827,14 +853,14 @@ static void check_temperature(struct thr_info *thr)
 			if (modminer->temp < MODMINER_RECOVER_TEMP) {
 				state->overheated = false;
 				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);
 			}
 		}
 		else if (modminer->temp >= MODMINER_OVERHEAT_TEMP) {
 			if (modminer->temp >= MODMINER_CUTOFF_TEMP) {
 				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_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);
 			} else {
 				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);
 
 				// 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
 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 modminer_fpga_state *state = thr->cgpu_data;
-	struct work *work = &state->running_work;
 	struct timeval now;
 	char cmd[2];
 	uint32_t nonce;
@@ -883,6 +908,10 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 	double timeout;
 	int temploop;
 
+	// Device is gone
+	if (modminer->usbinfo.nodev)
+		return -1;
+
 	// If we are overheated it will just keep checking for results
 	// since we can't stop the work
 	// 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;
 	temploop = 0;
-	while (1) {
+	while (0x80085) {
 		mutex_lock(modminer->modminer_mutex);
 		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);
 
 			// timeoutloop never resets so the timeouts can't
 			// 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++;
 				goto tryagain;
 			}
 
 			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;
 		}
@@ -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)",
-				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)) {
@@ -975,7 +1001,7 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 				if (state->death_stage_one) {
 					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true);
 					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
 					state->last_nonce.tv_sec = 0;
@@ -985,7 +1011,7 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 				} else {
 					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true);
 					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;
 				}
@@ -998,8 +1024,8 @@ tryagain:
 			break;
 
 		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);
 				temploop = 0;
 			}
@@ -1015,7 +1041,8 @@ tryagain:
 		if (tdiff(&now, &state->tv_workstart) > timeout)
 			break;
 
-		nmsleep(10);
+		// 1/10th sec to lower CPU usage
+		nmsleep(100);
 		if (work_restart(thr))
 			break;
 	}
@@ -1029,35 +1056,37 @@ tryagain:
 	// Overheat will complete the nonce range
 	if (hashes > 0xffffffff)
 		hashes = 0xffffffff;
-	else
-	if (hashes <= state->hashes)
-		hashes = 1;
-	else
-		hashes -= state->hashes;
-	state->hashes += hashes;
+
+	work->blk.nonce = 0xffffffff;
+
 	return hashes;
 }
 
 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;
-	int64_t hashes = 0;
-	bool startwork;
 	struct timeval tv1, tv2;
+	int64_t hashes;
+
+	// Device is gone
+	if (thr->cgpu->usbinfo.nodev)
+		return -1;
 
 	// Don't start new work if overheated
 	if (state->overheated == true) {
 		gettimeofday(&tv1, NULL);
-		if (state->work_running)
-			state->work_running = false;
 
 		while (state->overheated == true) {
 			check_temperature(thr);
 
+			// Device is gone
+			if (thr->cgpu->usbinfo.nodev)
+				return -1;
+
 			if (state->overheated == true) {
 				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)
 					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;
 }
 
@@ -1141,10 +1156,11 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char
 	return replybuf;
 }
 
-struct device_api modminer_api = {
-	.dname = "modminer",
+struct device_drv modminer_drv = {
+	.drv_id = DRIVER_MODMINER,
+	.dname = "ModMiner",
 	.name = "MMQ",
-	.api_detect = modminer_detect,
+	.drv_detect = modminer_detect,
 	.get_statline_before = get_modminer_statline_before,
 	.set_device = modminer_set_device,
 	.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 have_opencl;
 
-
-
 extern void *miner_thread(void *userdata);
 extern int dev_from_id(int thr_id);
 extern void tailsprintf(char *f, const char *fmt, ...);
 extern void wlog(const char *f, ...);
 extern void decay_time(double *f, double fadd);
 
-
 /**********************************************/
 
+#ifdef HAVE_OPENCL
+struct device_drv opencl_drv;
+#endif
+
 #ifdef HAVE_ADL
 extern float gpu_temp(int gpu);
 extern int gpu_fanspeed(int gpu);
 extern int gpu_fanpercent(int gpu);
 #endif
 
-
 #ifdef HAVE_OPENCL
 char *set_vector(char *arg)
 {
@@ -591,28 +591,20 @@ char *set_intensity(char *arg)
 
 	return NULL;
 }
-#endif
-
-
-#ifdef HAVE_OPENCL
-struct device_api opencl_api;
 
 char *print_ndevs_and_exit(int *ndevs)
 {
 	opt_log_output = true;
-	opencl_api.api_detect();
+	opencl_drv.drv_detect();
 	clear_adl(*ndevs);
 	applog(LOG_INFO, "%i GPU devices max detected", *ndevs);
 	exit(*ndevs);
 }
 #endif
 
-
 struct cgpu_info gpus[MAX_GPUDEVICES]; /* Maximum number apparently possible */
 struct cgpu_info *cpus;
 
-
-
 #ifdef HAVE_OPENCL
 
 /* 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;
 
 	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) {
 			applog(LOG_WARNING, "Disabling extra threads due to dynamic mode.");
 			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 */
 
 #if defined(HAVE_OPENCL) && defined(HAVE_CURSES)
@@ -716,7 +706,7 @@ retry:
 		else
 			wlog("%d\n", gpus[gpu].intensity);
 		for (i = 0; i < mining_threads; i++) {
-			thr = &thr_info[i];
+			thr = get_thread(i);
 			if (thr->cgpu != cgpu)
 				continue;
 			get_datestamp(checkin, &thr->last);
@@ -771,9 +761,9 @@ retry:
 		}
 		gpus[selected].deven = DEV_ENABLED;
 		for (i = 0; i < mining_threads; ++i) {
-			thr = &thr_info[i];
+			thr = get_thread(i);
 			cgpu = thr->cgpu;
-			if (cgpu->api != &opencl_api)
+			if (cgpu->drv->drv_id != DRIVER_OPENCL)
 				continue;
 			if (dev_from_id(i) != selected)
 				continue;
@@ -1158,14 +1148,14 @@ select_cgpu:
 	gpu = cgpu->device_id;
 
 	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
-		thr = &thr_info[thr_id];
+		thr = get_thread(thr_id);
 		cgpu = thr->cgpu;
-		if (cgpu->api != &opencl_api)
+		if (cgpu->drv->drv_id != DRIVER_OPENCL)
 			continue;
 		if (dev_from_id(thr_id) != gpu)
 			continue;
 
-		thr = &thr_info[thr_id];
+		thr = get_thread(thr_id);
 		if (!thr) {
 			applog(LOG_WARNING, "No reference to thread %d exists", thr_id);
 			continue;
@@ -1183,9 +1173,9 @@ select_cgpu:
 	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
 		int virtual_gpu;
 
-		thr = &thr_info[thr_id];
+		thr = get_thread(thr_id);
 		cgpu = thr->cgpu;
-		if (cgpu->api != &opencl_api)
+		if (cgpu->drv->drv_id != DRIVER_OPENCL)
 			continue;
 		if (dev_from_id(thr_id) != gpu)
 			continue;
@@ -1220,9 +1210,9 @@ select_cgpu:
 	get_datestamp(cgpu->init, &now);
 
 	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
-		thr = &thr_info[thr_id];
+		thr = get_thread(thr_id);
 		cgpu = thr->cgpu;
-		if (cgpu->api != &opencl_api)
+		if (cgpu->drv->drv_id != DRIVER_OPENCL)
 			continue;
 		if (dev_from_id(thr_id) != gpu)
 			continue;
@@ -1243,8 +1233,6 @@ void *reinit_gpu(__maybe_unused void *userdata)
 
 
 #ifdef HAVE_OPENCL
-struct device_api opencl_api;
-
 static void opencl_detect()
 {
 	int i;
@@ -1263,7 +1251,7 @@ static void opencl_detect()
 
 		cgpu = &gpus[i];
 		cgpu->deven = DEV_ENABLED;
-		cgpu->api = &opencl_api;
+		cgpu->drv = &opencl_drv;
 		cgpu->device_id = i;
 		cgpu->threads = opt_g_threads;
 		cgpu->virtual_gpu = i;
@@ -1276,7 +1264,7 @@ static void opencl_detect()
 
 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
@@ -1299,7 +1287,8 @@ static void get_opencl_statline_before(char *buf, struct cgpu_info *gpu)
 		else
 			tailsprintf(buf, "        ");
 		tailsprintf(buf, "| ");
-	}
+	} else
+		gpu->drv->get_statline_before = &blank_get_statline_before;
 }
 #endif
 
@@ -1570,10 +1559,11 @@ static void opencl_thread_shutdown(struct thr_info *thr)
 	clReleaseContext(clState->context);
 }
 
-struct device_api opencl_api = {
+struct device_drv opencl_drv = {
+	.drv_id = DRIVER_OPENCL,
 	.dname = "opencl",
 	.name = "GPU",
-	.api_detect = opencl_detect,
+	.drv_detect = opencl_detect,
 	.reinit_device = reinit_opencl_device,
 #ifdef HAVE_ADL
 	.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 int opt_platform_id;
 
-extern struct device_api opencl_api;
+extern struct device_drv opencl_drv;
 
 #endif /* __DEVICE_GPU_H__ */

+ 9 - 6
driver-ztex.c

@@ -30,7 +30,7 @@
 
 #define GOLDEN_BACKLOG 5
 
-struct device_api ztex_api;
+struct device_drv ztex_drv;
 
 // Forward declarations
 static void ztex_disable(struct thr_info* thr);
@@ -68,7 +68,7 @@ static void ztex_detect(void)
 
 	for (i = 0; i < cnt; i++) {
 		ztex = calloc(1, sizeof(struct cgpu_info));
-		ztex->api = &ztex_api;
+		ztex->drv = &ztex_drv;
 		ztex->device_ztex = ztex_devices[i]->dev;
 		ztex->threads = 1;
 		ztex->device_ztex->fpgaNum = 0;
@@ -82,7 +82,7 @@ static void ztex_detect(void)
 
 		for (j = 1; j < fpgacount; j++) {
 			ztex = calloc(1, sizeof(struct cgpu_info));
-			ztex->api = &ztex_api;
+			ztex->drv = &ztex_drv;
 			ztex_slave = calloc(1, sizeof(struct libztex_device));
 			memcpy(ztex_slave, ztex_devices[i]->dev, sizeof(struct libztex_device));
 			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)
 {
+	struct cgpu_info *cgpu;
+
 	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);
 }
 
-struct device_api ztex_api = {
+struct device_drv ztex_drv = {
 	.dname = "ztex",
 	.name = "ZTX",
-	.api_detect = ztex_detect,
+	.drv_detect = ztex_detect,
 	.get_statline_before = ztex_statline_before,
 	.thread_prepare = ztex_prepare,
 	.scanhash = ztex_scanhash,

+ 1 - 1
findnonce.c

@@ -204,7 +204,7 @@ static void *postcalc_hash(void *userdata)
 	 * end of the res[] array */
 	if (unlikely(pcd->res[FOUND] & ~FOUND)) {
 		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++;
 		thr->cgpu->hw_errors++;
 		pcd->res[FOUND] &= FOUND;

+ 5 - 5
fpgautils.c

@@ -104,14 +104,14 @@ int serial_autodetect_devserial(__maybe_unused detectone_func_t detectone, __may
 #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;
 	const char *dev, *colon;
 	bool inhibitauto = false;
 	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) {
 		dev = iter->string;
@@ -119,8 +119,8 @@ int _serial_detect(struct device_api *api, detectone_func_t detectone, autoscan_
 			size_t idlen = colon - dev;
 
 			// 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;
 
 			dev = colon + 1;

+ 7 - 7
fpgautils.h

@@ -16,13 +16,13 @@
 typedef bool(*detectone_func_t)(const char*);
 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_udev(detectone_func_t, const char *prodname);
 

+ 42 - 8
miner.h

@@ -114,7 +114,7 @@ static inline int fsync (int fd)
   #include "libztex.h"
 #endif
 
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
   #include "usbutils.h"
 #endif
 
@@ -196,6 +196,15 @@ static inline int fsync (int fd)
 #endif
 #endif
 
+enum drv_driver {
+	DRIVER_OPENCL,
+	DRIVER_ICARUS,
+	DRIVER_BITFORCE,
+	DRIVER_MODMINER,
+	DRIVER_ZTEX,
+	DRIVER_CPU,
+};
+
 enum alive {
 	LIFE_WELL,
 	LIFE_SICK,
@@ -259,16 +268,20 @@ struct gpu_adl {
 };
 #endif
 
+extern void blank_get_statline_before(char *buf, struct cgpu_info __maybe_unused *cgpu);
+
 struct api_data;
 struct thr_info;
 struct work;
 
-struct device_api {
+struct device_drv {
+	enum drv_driver drv_id;
+
 	char *dname;
 	char *name;
 
-	// API-global functions
-	void (*api_detect)();
+	// DRV-global functions
+	void (*drv_detect)();
 
 	// Device-specific functions
 	void (*reinit_device)(struct cgpu_info *);
@@ -288,8 +301,13 @@ struct device_api {
 	void (*hw_error)(struct thr_info *);
 	void (*thread_shutdown)(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 {
 	DEV_ENABLED,
 	DEV_DISABLED,
@@ -359,13 +377,15 @@ struct cgminer_pool_stats {
 	uint32_t max_diff_count;
 	uint64_t times_sent;
 	uint64_t bytes_sent;
+	uint64_t net_bytes_sent;
 	uint64_t times_received;
 	uint64_t bytes_received;
+	uint64_t net_bytes_received;
 };
 
 struct cgpu_info {
 	int cgminer_id;
-	struct device_api *api;
+	struct device_drv *drv;
 	int device_id;
 	char *name;
 	char *device_path;
@@ -374,13 +394,17 @@ struct cgpu_info {
 #ifdef USE_ZTEX
 		struct libztex_device *device_ztex;
 #endif
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 		struct cg_usb_device *usbdev;
 #endif
+#ifdef USE_ICARUS
 		int device_fd;
+#endif
 	};
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
+	struct cg_usb_info usbinfo;
+#endif
 #ifdef USE_MODMINER
-	int usbstat;
 	char fpgaid;
 	unsigned char clock;
 	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 console_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_cond_t restart_cond;
@@ -740,6 +766,7 @@ extern void api(int thr_id);
 extern struct pool *current_pool(void);
 extern int enabled_pools;
 extern bool detect_stratum(struct pool *pool, char *url);
+extern void print_summary(void);
 extern struct pool *add_pool(void);
 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"
 #endif
 
+extern bool hotplug_mode;
 extern struct list_head scan_devices;
 extern int nDevs;
 extern int opt_n_threads;
@@ -762,7 +790,8 @@ extern int num_processors;
 extern int hw_errors;
 extern bool use_syslog;
 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 int gpu_threads;
 #ifdef USE_SCRYPT
@@ -926,6 +955,7 @@ struct pool {
 
 	time_t last_share_time;
 	double last_share_diff;
+	uint64_t best_diff;
 
 	struct cgminer_stats cgminer_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 remove_pool(struct pool *pool);
 extern void write_config(FILE *fcfg);
+extern void zero_bestshare(void);
+extern void zero_stats(void);
 extern void default_save_file(char *filename);
 extern bool log_curses_only(int prio, const char *f, va_list ap);
 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 __copy_work(struct work *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 {
 	API_ESCAPE,

+ 239 - 50
miner.php

@@ -8,7 +8,7 @@ global $checklastshare, $poolinputs, $hidefields;
 global $ignorerefresh, $changerefresh, $autorefresh;
 global $allowcustompages, $customsummarypages;
 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
 # to configure miner.php
@@ -20,6 +20,9 @@ $title = 'Mine';
 # Set $readonly to false then it will check cgminer 'privileged'
 $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 true to attempt to display the notify command
 $notify = true;
@@ -113,16 +116,17 @@ $poolspage = array(
 			'POOL.Difficulty Rejected=Diff Rej',
 			'POOL.Has Stratum=Stratum', 'POOL.Stratum Active=StrAct',
 			'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(
  'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted',
 			'Rejected', 'Utility', 'Hardware Errors',
 			'Work Utility'),
  '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(
  'POOL+STATS' => array(
@@ -130,7 +134,8 @@ $poolsext = array(
 	'group' => array('POOL.URL', 'POOL.Has Stratum', 'POOL.Stratum Active', 'POOL.Has GBT'),
 	'calc' => array('POOL.Difficulty Accepted' => 'sum', 'POOL.Difficulty Rejected' => '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)))
 );
 
@@ -212,6 +217,10 @@ $rigerror = array();
 global $rownum;
 $rownum = 0;
 #
+// Login
+global $ses;
+$ses = 'rutroh';
+#
 function getcss($cssname, $dom = false)
 {
  global $colourtable, $colouroverride;
@@ -239,7 +248,7 @@ function getdom($domname)
  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 $error, $readonly, $poolinputs, $here;
@@ -285,8 +294,10 @@ td.lst { $miner_font ".getcss('td.lst')."}
 td.hi { $miner_font ".getcss('td.hi')."}
 td.lo { $miner_font ".getcss('td.lo')."}
 </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";
 
 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)
 		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'>
 <tr><td align=center valign=top>
 <table border=0 cellpadding=4 cellspacing=0 summary='Mine'>
@@ -578,6 +590,14 @@ function classlastshare($when, $alldata, $warnclass, $errorclass)
  return '';
 }
 #
+function endzero($num)
+{
+ $rep = preg_replace('/0*$/', '', $num);
+ if ($rep === '')
+	$rep = '0';
+ return $rep;
+}
+#
 function fmt($section, $name, $value, $when, $alldata)
 {
  global $dfmt, $rownum;
@@ -840,12 +860,16 @@ function fmt($section, $name, $value, $when, $alldata)
 	case 'total.Diff1 Work':
 	case 'STATS.Times Sent':
 	case 'STATS.Bytes Sent':
+	case 'STATS.Net Bytes Sent':
 	case 'STATS.Times Recv':
 	case 'STATS.Bytes Recv':
+	case 'STATS.Net Bytes Recv':
 	case 'total.Times Sent':
 	case 'total.Bytes Sent':
+	case 'total.Net Bytes Sent':
 	case 'total.Times Recv':
 	case 'total.Bytes Recv':
+	case 'total.Net Bytes Recv':
 		$parts = explode('.', $value, 2);
 		if (count($parts) == 1)
 			$dec = '';
@@ -853,6 +877,23 @@ function fmt($section, $name, $value, $when, $alldata)
 			$dec = '.'.$parts[1];
 		$ret = number_format((float)$parts[0]).$dec;
 		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 'PGA.Status':
 	case 'DEVS.Status':
@@ -1507,7 +1548,6 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
 #
 function refreshbuttons()
 {
- global $readonly;
  global $ignorerefresh, $changerefresh, $autorefresh;
 
  if ($ignorerefresh == false && $changerefresh == true)
@@ -1521,7 +1561,7 @@ function refreshbuttons()
 #
 function pagebuttons($rig, $pg)
 {
- global $readonly, $rigs;
+ global $readonly, $rigs, $userlist, $ses;
  global $allowcustompages, $customsummarypages;
 
  if ($rig === null)
@@ -1557,18 +1597,33 @@ function pagebuttons($rig, $pg)
  }
 
  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)
-	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 '</td><td width=100%>&nbsp;</td><td nowrap>';
  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\")'>";
  }
  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>";
 }
 #
@@ -2015,6 +2076,8 @@ function docalc($func, $data)
 			if (strcasecmp($val, $ans) > 0)
 				$ans = $val;
 	return $ans;
+ case 'count':
+	return count($data);
  case 'any':
  default:
 	return $data[0];
@@ -2410,13 +2473,126 @@ function showcustompage($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()
 {
  global $miner, $port;
  global $readonly, $notify, $rigs;
  global $ignorerefresh, $autorefresh;
- global $allowcustompages;
+ global $allowcustompages, $customsummarypages;
  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)
  {
@@ -2425,52 +2601,65 @@ function display()
 		$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)
  {
 	$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);
 		return;
 	}
  }
 
+ if ($pagesonly === true)
+ {
+	onlylogin();
+	return;
+ }
+
  if (count($rigs) == 1)
  {
 	$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
  * under the terms of the GNU General Public License as published by the Free
@@ -12,6 +12,29 @@
 
 #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
 #define DEVTIMEOUT 0
 
@@ -32,6 +55,7 @@ struct usb_find_devices {
 	const char *name;
 	uint16_t idVendor;
 	uint16_t idProduct;
+	int kernel;
 	int config;
 	int interface;
 	unsigned int timeout;
@@ -44,8 +68,6 @@ struct cg_usb_device {
 	libusb_device_handle *handle;
 	pthread_mutex_t *mutex;
 	struct libusb_device_descriptor *descriptor;
-	uint8_t bus_number;
-	uint8_t device_address;
 	uint16_t usbver;
 	int speed;
 	char *prod_string;
@@ -55,8 +77,18 @@ struct cg_usb_device {
 	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 {
-	C_PING = 0,
+	C_REJECTED = 0,
+	C_PING,
 	C_CLEAR,
 	C_REQUESTVERSION,
 	C_GETVERSION,
@@ -78,32 +110,48 @@ enum usb_cmds {
 	C_SENDWORKSTATUS,
 	C_REQUESTWORKSTATUS,
 	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
 };
 
-struct device_api;
+struct device_drv;
 struct cgpu_info;
 
 void usb_uninit(struct cgpu_info *cgpu);
 bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found);
-void usb_detect(struct device_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);
 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_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();
 
 #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) \
-	_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) \
-	_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) \
-	_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) \
 	_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) \
 	_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

+ 30 - 4
util.c

@@ -261,6 +261,29 @@ static void set_nettime(void)
 	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,
 		      const char *userpass, const char *rpc_req,
 		      bool probe, bool longpoll, int *rolltime,
@@ -287,10 +310,11 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 		probing = !pool->probed;
 	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_URL, url);
 	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.bytes_sent += ssent;
+	pool->cgminer_pool_stats.net_bytes_sent += ssent;
 	return true;
 }
 
@@ -1041,6 +1066,7 @@ char *recv_line(struct pool *pool)
 
 	pool->cgminer_pool_stats.times_received++;
 	pool->cgminer_pool_stats.bytes_received += len;
+	pool->cgminer_pool_stats.net_bytes_received += len;
 out:
 	if (!sret)
 		clear_sock(pool);

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