Browse Source

Merge branch 'hotplug2' into bfgminer

Luke Dashjr 12 years ago
parent
commit
cce9df8fb6
18 changed files with 488 additions and 185 deletions
  1. 11 0
      README.RPC
  2. 31 0
      api.c
  3. 3 9
      deviceapi.c
  4. 3 0
      driver-avalon.c
  5. 3 0
      driver-bitforce.c
  6. 17 5
      driver-cpu.c
  7. 1 4
      driver-icarus.c
  8. 3 0
      driver-modminer.c
  9. 14 3
      driver-opencl.c
  10. 3 0
      driver-x6500.c
  11. 2 0
      driver-ztex.c
  12. 80 21
      fpgautils.c
  13. 7 1
      fpgautils.h
  14. 10 0
      ft232r.c
  15. 11 0
      libztex.c
  16. 278 142
      miner.c
  17. 3 0
      miner.h
  18. 8 0
      util.h

+ 11 - 0
README.RPC

@@ -145,6 +145,10 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               Will not report PGAs if PGA mining is disabled
                               Will not report PGAs if PGA mining is disabled
                               Will not report CPUs if CPU mining is disabled
                               Will not report CPUs if CPU mining is disabled
 
 
+ devscan|info  DEVS           Probes for a device specified by info, which is
+                              the same format as the --scan-serial command line
+                              option
+
  devdetail     DEVS           Each available device with their fixed details
  devdetail     DEVS           Each available device with their fixed details
                               e.g. GPU=0,Driver=opencl,Kernel=diablo,Model=...|
                               e.g. GPU=0,Driver=opencl,Kernel=diablo,Model=...|
 
 
@@ -410,6 +414,13 @@ api-example.py - a Python script to access the API
 Feature Changelog for external applications using the API:
 Feature Changelog for external applications using the API:
 
 
 
 
+API V1.25.1
+
+Added API commands:
+ 'devscan'
+
+----------
+
 API V1.25 (BFGMiner v3.0.1)
 API V1.25 (BFGMiner v3.0.1)
 
 
 Modified API commands:
 Modified API commands:

+ 31 - 0
api.c

@@ -320,6 +320,8 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_ZERSUM 96
 #define MSG_ZERSUM 96
 #define MSG_ZERNOSUM 97
 #define MSG_ZERNOSUM 97
 
 
+#define MSG_DEVSCAN 0x100
+
 enum code_severity {
 enum code_severity {
 	SEVERITY_ERR,
 	SEVERITY_ERR,
 	SEVERITY_WARN,
 	SEVERITY_WARN,
@@ -329,6 +331,7 @@ enum code_severity {
 };
 };
 
 
 enum code_parameters {
 enum code_parameters {
+	PARAM_COUNT,
 	PARAM_GPU,
 	PARAM_GPU,
 	PARAM_PGA,
 	PARAM_PGA,
 	PARAM_CPU,
 	PARAM_CPU,
@@ -494,6 +497,7 @@ struct CODES {
  { SEVERITY_ERR,   MSG_ZERINV,	PARAM_STR,	"Invalid zero parameter '%s'" },
  { SEVERITY_ERR,   MSG_ZERINV,	PARAM_STR,	"Invalid zero parameter '%s'" },
  { SEVERITY_SUCC,  MSG_ZERSUM,	PARAM_STR,	"Zeroed %s stats with summary" },
  { SEVERITY_SUCC,  MSG_ZERSUM,	PARAM_STR,	"Zeroed %s stats with summary" },
  { SEVERITY_SUCC,  MSG_ZERNOSUM, PARAM_STR,	"Zeroed %s stats without summary" },
  { SEVERITY_SUCC,  MSG_ZERNOSUM, PARAM_STR,	"Zeroed %s stats without summary" },
+ { SEVERITY_SUCC,  MSG_DEVSCAN, PARAM_COUNT,	"Added %d new device(s)" },
  { SEVERITY_FAIL, 0, 0, NULL }
  { SEVERITY_FAIL, 0, 0, NULL }
 };
 };
 
 
@@ -1184,6 +1188,7 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p
 			severity[1] = '\0';
 			severity[1] = '\0';
 
 
 			switch(codes[i].params) {
 			switch(codes[i].params) {
+				case PARAM_COUNT:
 				case PARAM_GPU:
 				case PARAM_GPU:
 				case PARAM_PGA:
 				case PARAM_PGA:
 				case PARAM_CPU:
 				case PARAM_CPU:
@@ -1583,6 +1588,31 @@ static void gpudev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *p
 }
 }
 #endif
 #endif
 
 
+static void devscan(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	int n;
+	bool io_open = false;
+	
+	applog(LOG_DEBUG, "RPC: request to scan %s for devices",
+	       param);
+	
+	if (param && !param[0])
+		param = NULL;
+	
+	n = scan_serial(param);
+	
+	message(io_data, MSG_DEVSCAN, n, NULL, isjson);
+	
+	io_open = io_add(io_data, isjson ? COMSTR JSON_DEVS : _DEVS COMSTR);
+
+	n = total_devices - n;
+	for (int i = n; i < total_devices; ++i)
+		devdetail_an(io_data, get_devices(i), isjson, i > n);
+	
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
 #ifdef HAVE_AN_FPGA
 #ifdef HAVE_AN_FPGA
 static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 {
@@ -3072,6 +3102,7 @@ struct CMDS {
 } cmds[] = {
 } cmds[] = {
 	{ "version",		apiversion,	false },
 	{ "version",		apiversion,	false },
 	{ "config",		minerconfig,	false },
 	{ "config",		minerconfig,	false },
+	{ "devscan",		devscan,	false },
 	{ "devs",		devstatus,	false },
 	{ "devs",		devstatus,	false },
 	{ "devdetail",	devdetail,	false },
 	{ "devdetail",	devdetail,	false },
 	{ "pools",		poolstatus,	false },
 	{ "pools",		poolstatus,	false },

+ 3 - 9
deviceapi.c

@@ -608,10 +608,8 @@ bool add_cgpu(struct cgpu_info *cgpu)
 	strcpy(cgpu->proc_repr, cgpu->dev_repr);
 	strcpy(cgpu->proc_repr, cgpu->dev_repr);
 	sprintf(cgpu->proc_repr_ns, "%s%u", cgpu->drv->name, cgpu->device_id);
 	sprintf(cgpu->proc_repr_ns, "%s%u", cgpu->drv->name, cgpu->device_id);
 	
 	
-	wr_lock(&devices_lock);
-	
-	devices = realloc(devices, sizeof(struct cgpu_info *) * (total_devices + lpcount + 1));
-	devices[total_devices++] = cgpu;
+	devices_new = realloc(devices_new, sizeof(struct cgpu_info *) * (total_devices_new + lpcount + 1));
+	devices_new[total_devices_new++] = cgpu;
 	
 	
 	if (lpcount > 1)
 	if (lpcount > 1)
 	{
 	{
@@ -645,7 +643,7 @@ bool add_cgpu(struct cgpu_info *cgpu)
 				slave->proc_repr_ns[ns] += i;
 				slave->proc_repr_ns[ns] += i;
 			}
 			}
 			slave->threads = tpp;
 			slave->threads = tpp;
-			devices[total_devices++] = slave;
+			devices_new[total_devices_new++] = slave;
 			*nlp_p = slave;
 			*nlp_p = slave;
 			nlp_p = &slave->next_proc;
 			nlp_p = &slave->next_proc;
 		}
 		}
@@ -653,12 +651,8 @@ bool add_cgpu(struct cgpu_info *cgpu)
 		cgpu->proc_id = 0;
 		cgpu->proc_id = 0;
 		cgpu->threads -= (tpp * (lpcount - 1));
 		cgpu->threads -= (tpp * (lpcount - 1));
 	}
 	}
-	
-	wr_unlock(&devices_lock);
 
 
-	mutex_lock(&stats_lock);
 	cgpu->last_device_valid_work = time(NULL);
 	cgpu->last_device_valid_work = time(NULL);
-	mutex_unlock(&stats_lock);
 	
 	
 	return true;
 	return true;
 }
 }

+ 3 - 0
driver-avalon.c

@@ -571,6 +571,9 @@ static bool avalon_detect_one(const char *devpath)
 	int baud, miner_count, asic_count, timeout, frequency = 0;
 	int baud, miner_count, asic_count, timeout, frequency = 0;
 	struct cgpu_info *avalon;
 	struct cgpu_info *avalon;
 
 
+	if (serial_claim(devpath, avalon_drv))
+		return false;
+	
 	int this_option_offset = ++option_offset;
 	int this_option_offset = ++option_offset;
 	get_options(this_option_offset, &baud, &miner_count, &asic_count,
 	get_options(this_option_offset, &baud, &miner_count, &asic_count,
 		    &timeout, &frequency);
 		    &timeout, &frequency);

+ 3 - 0
driver-bitforce.c

@@ -190,6 +190,9 @@ static bool bitforce_detect_one(const char *devpath)
 		return false;
 		return false;
 	}
 	}
 
 
+	if (serial_claim_v(devpath, &bitforce_drv))
+		return false;
+	
 	applog(LOG_DEBUG, "Found BitForce device on %s", devpath);
 	applog(LOG_DEBUG, "Found BitForce device on %s", devpath);
 	initdata = malloc(sizeof(*initdata));
 	initdata = malloc(sizeof(*initdata));
 	*initdata = (struct bitforce_init_data){
 	*initdata = (struct bitforce_init_data){

+ 17 - 5
driver-cpu.c

@@ -30,8 +30,10 @@
 #include <libgen.h>
 #include <libgen.h>
 
 
 #include "compat.h"
 #include "compat.h"
+#include "fpgautils.h"
 #include "miner.h"
 #include "miner.h"
 #include "bench_block.h"
 #include "bench_block.h"
+#include "util.h"
 #include "driver-cpu.h"
 #include "driver-cpu.h"
 
 
 #if defined(unix)
 #if defined(unix)
@@ -709,7 +711,7 @@ char *force_nthreads_int(const char *arg, int *i)
 #endif
 #endif
 
 
 #ifdef WANT_CPUMINE
 #ifdef WANT_CPUMINE
-static void cpu_detect()
+static int cpu_autodetect()
 {
 {
 	int i;
 	int i;
 
 
@@ -745,13 +747,10 @@ static void cpu_detect()
 	#endif /* !WIN32 */
 	#endif /* !WIN32 */
 
 
 	if (opt_n_threads < 0 || !forced_n_threads) {
 	if (opt_n_threads < 0 || !forced_n_threads) {
-		if (total_devices && !opt_usecpu)
-			opt_n_threads = 0;
-		else
 			opt_n_threads = num_processors;
 			opt_n_threads = num_processors;
 	}
 	}
 	if (num_processors < 1)
 	if (num_processors < 1)
-		return;
+		return 0;
 
 
 	cpus = calloc(opt_n_threads, sizeof(struct cgpu_info));
 	cpus = calloc(opt_n_threads, sizeof(struct cgpu_info));
 	if (unlikely(!cpus))
 	if (unlikely(!cpus))
@@ -767,6 +766,19 @@ static void cpu_detect()
 		cgpu->kname = algo_names[opt_algo];
 		cgpu->kname = algo_names[opt_algo];
 		add_cgpu(cgpu);
 		add_cgpu(cgpu);
 	}
 	}
+	return opt_n_threads;
+}
+
+static void cpu_detect()
+{
+	RUNONCE();
+	
+	if ((opt_n_threads < 0 || !forced_n_threads)
+	 && ((total_devices || total_devices_new) && !opt_usecpu))
+		// If there are any other devices, only act if the user has explicitly enabled it
+		noserial_detect_manual(&cpu_drv, cpu_autodetect);
+	else
+		noserial_detect(&cpu_drv, cpu_autodetect);
 }
 }
 
 
 static pthread_mutex_t cpualgo_lock;
 static pthread_mutex_t cpualgo_lock;

+ 1 - 4
driver-icarus.c

@@ -595,11 +595,8 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 			devpath, nonce_hex);
 			devpath, nonce_hex);
 	free(nonce_hex);
 	free(nonce_hex);
 
 
-	if (serial_claim(devpath, api)) {
-		const char *claimedby = serial_claim(devpath, api)->dname;
-		applog(LOG_DEBUG, "Icarus device %s already claimed by other driver: %s", devpath, claimedby);
+	if (serial_claim_v(devpath, api))
 		return false;
 		return false;
-	}
 
 
 	/* We have a real Icarus! */
 	/* We have a real Icarus! */
 	struct cgpu_info *icarus;
 	struct cgpu_info *icarus;

+ 3 - 0
driver-modminer.c

@@ -123,6 +123,9 @@ modminer_detect_one(const char *devpath)
 	char*devname = strdup(buf);
 	char*devname = strdup(buf);
 	applog(LOG_DEBUG, "ModMiner identified as: %s", devname);
 	applog(LOG_DEBUG, "ModMiner identified as: %s", devname);
 
 
+	if (serial_claim_v(devpath, &modminer_drv))
+		return false;
+	
 	if (1 != write(fd, MODMINER_FPGA_COUNT, 1))
 	if (1 != write(fd, MODMINER_FPGA_COUNT, 1))
 		bailout(LOG_DEBUG, "ModMiner detect: write failed on %s (get FPGA count)", devpath);
 		bailout(LOG_DEBUG, "ModMiner detect: write failed on %s (get FPGA count)", devpath);
 	len = read(fd, buf, 1);
 	len = read(fd, buf, 1);

+ 14 - 3
driver-opencl.c

@@ -39,6 +39,7 @@
 #define OMIT_OPENCL_API
 #define OMIT_OPENCL_API
 
 
 #include "compat.h"
 #include "compat.h"
+#include "fpgautils.h"
 #include "miner.h"
 #include "miner.h"
 #include "driver-opencl.h"
 #include "driver-opencl.h"
 #include "findnonce.h"
 #include "findnonce.h"
@@ -1408,7 +1409,7 @@ void *reinit_gpu(__maybe_unused void *userdata)
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
 struct device_drv opencl_api;
 struct device_drv opencl_api;
 
 
-static void opencl_detect()
+static int opencl_autodetect()
 {
 {
 #ifndef WIN32
 #ifndef WIN32
 	if (!getenv("DISPLAY")) {
 	if (!getenv("DISPLAY")) {
@@ -1419,7 +1420,7 @@ static void opencl_detect()
 
 
 	if (!load_opencl_symbols()) {
 	if (!load_opencl_symbols()) {
 		nDevs = 0;
 		nDevs = 0;
-		return;
+		return 0;
 	}
 	}
 
 
 
 
@@ -1432,7 +1433,7 @@ static void opencl_detect()
 	}
 	}
 
 
 	if (!nDevs)
 	if (!nDevs)
-		return;
+		return 0;
 
 
 	/* If opt_g_threads is not set, use default 1 thread on scrypt and
 	/* If opt_g_threads is not set, use default 1 thread on scrypt and
 	 * 2 for regular mining */
 	 * 2 for regular mining */
@@ -1478,6 +1479,16 @@ static void opencl_detect()
 
 
 	if (!opt_noadl)
 	if (!opt_noadl)
 		init_adl(nDevs);
 		init_adl(nDevs);
+	
+	return nDevs;
+}
+
+static void opencl_detect()
+{
+	RUNONCE();
+	
+	// This wrapper ensures users can specify -S opencl:noauto to disable it
+	noserial_detect(&opencl_api, opencl_autodetect);
 }
 }
 
 
 static void reinit_opencl_device(struct cgpu_info *gpu)
 static void reinit_opencl_device(struct cgpu_info *gpu)

+ 3 - 0
driver-x6500.c

@@ -124,6 +124,9 @@ uint32_t x6500_get_register(struct jtag_port *jp, uint8_t addr)
 
 
 static bool x6500_foundusb(libusb_device *dev, const char *product, const char *serial)
 static bool x6500_foundusb(libusb_device *dev, const char *product, const char *serial)
 {
 {
+	if (bfg_claim_libusb(&x6500_api, true, dev))
+		return false;
+	
 	struct cgpu_info *x6500;
 	struct cgpu_info *x6500;
 	x6500 = calloc(1, sizeof(*x6500));
 	x6500 = calloc(1, sizeof(*x6500));
 	x6500->drv = &x6500_api;
 	x6500->drv = &x6500_api;

+ 2 - 0
driver-ztex.c

@@ -85,6 +85,8 @@ static int ztex_autodetect(void)
 
 
 	for (i = 0; i < cnt; i++) {
 	for (i = 0; i < cnt; i++) {
 		ztex_master = ztex_devices[i]->dev;
 		ztex_master = ztex_devices[i]->dev;
+		if (bfg_claim_usb(&ztex_drv, true, ztex_master->usbbus, ztex_master->usbaddress))
+			return false;
 		ztex_master->root = ztex_master;
 		ztex_master->root = ztex_master;
 		fpgacount = libztex_numberOfFpgas(ztex_master);
 		fpgacount = libztex_numberOfFpgas(ztex_master);
 		ztex_master->handles = fpgacount;
 		ztex_master->handles = fpgacount;

+ 80 - 21
fpgautils.c

@@ -100,6 +100,18 @@ bool search_needles(const char *haystack, va_list needles)
 
 
 #define SEARCH_NEEDLES(haystack)  search_needles(haystack, needles)
 #define SEARCH_NEEDLES(haystack)  search_needles(haystack, needles)
 
 
+static
+int _detectone_wrap(const detectone_func_t detectone, const char * const param, const char *fname)
+{
+	if (bfg_claim_serial(NULL, false, param))
+	{
+		applog(LOG_DEBUG, "%s: %s is already claimed, skipping probe", fname, param);
+		return 0;
+	}
+	return detectone(param);
+}
+#define detectone(param)  _detectone_wrap(detectone, param, __func__)
+
 #ifdef HAVE_LIBUDEV
 #ifdef HAVE_LIBUDEV
 static
 static
 int _serial_autodetect_udev(detectone_func_t detectone, va_list needles)
 int _serial_autodetect_udev(detectone_func_t detectone, va_list needles)
@@ -331,6 +343,8 @@ out:
 #	define _serial_autodetect_ftdi(...)  (0)
 #	define _serial_autodetect_ftdi(...)  (0)
 #endif
 #endif
 
 
+#undef detectone
+
 int _serial_autodetect(detectone_func_t detectone, ...)
 int _serial_autodetect(detectone_func_t detectone, ...)
 {
 {
 	int rv;
 	int rv;
@@ -347,13 +361,11 @@ int _serial_autodetect(detectone_func_t detectone, ...)
 	return rv;
 	return rv;
 }
 }
 
 
-struct device_drv *serial_claim(const char *devpath, struct device_drv *api);
-
 int _serial_detect(struct device_drv *api, detectone_func_t detectone, autoscan_func_t autoscan, int flags)
 int _serial_detect(struct device_drv *api, detectone_func_t detectone, autoscan_func_t autoscan, int flags)
 {
 {
 	struct string_elist *iter, *tmp;
 	struct string_elist *iter, *tmp;
 	const char *dev, *colon;
 	const char *dev, *colon;
-	bool inhibitauto = false;
+	bool inhibitauto = flags & 4;
 	char found = 0;
 	char found = 0;
 	bool forceauto = flags & 1;
 	bool forceauto = flags & 1;
 	bool hasname;
 	bool hasname;
@@ -404,11 +416,27 @@ int _serial_detect(struct device_drv *api, detectone_func_t detectone, autoscan_
 	return found;
 	return found;
 }
 }
 
 
+enum bfg_device_bus {
+	BDB_SERIAL,
+	BDB_USB,
+};
+
+// TODO: claim USB side of USB-Serial devices
+typedef
+struct my_dev_t {
+	enum bfg_device_bus bus;
+	union {
+		struct {
+			uint8_t usbbus;
+			uint8_t usbaddr;
+		};
 #ifndef WIN32
 #ifndef WIN32
-typedef dev_t my_dev_t;
+		dev_t dev;
 #else
 #else
-typedef int my_dev_t;
+		int com;
 #endif
 #endif
+	};
+} my_dev_t;
 
 
 struct _device_claim {
 struct _device_claim {
 	struct device_drv *drv;
 	struct device_drv *drv;
@@ -416,42 +444,73 @@ struct _device_claim {
 	UT_hash_handle hh;
 	UT_hash_handle hh;
 };
 };
 
 
-struct device_drv *serial_claim(const char *devpath, struct device_drv *api)
+static
+struct device_drv *bfg_claim_any(struct device_drv * const api, const char * const verbose, const my_dev_t * const dev)
 {
 {
 	static struct _device_claim *claims = NULL;
 	static struct _device_claim *claims = NULL;
 	struct _device_claim *c;
 	struct _device_claim *c;
-	my_dev_t dev;
+	
+	HASH_FIND(hh, claims, dev, sizeof(*dev), c);
+	if (c)
+	{
+		if (verbose)
+			applog(LOG_DEBUG, "%s device %s already claimed by other driver: %s",
+			       api->dname, verbose, c->drv->dname);
+		return c->drv;
+	}
+	
+	if (!api)
+		return NULL;
+	
+	c = malloc(sizeof(*c));
+	c->dev = *dev;
+	c->drv = api;
+	HASH_ADD(hh, claims, dev, sizeof(*dev), c);
+	return NULL;
+}
 
 
+struct device_drv *bfg_claim_serial(struct device_drv * const api, const bool verbose, const char * const devpath)
+{
+	my_dev_t dev;
+	
+	dev.bus = BDB_SERIAL;
 #ifndef WIN32
 #ifndef WIN32
 	{
 	{
 		struct stat my_stat;
 		struct stat my_stat;
 		if (stat(devpath, &my_stat))
 		if (stat(devpath, &my_stat))
 			return NULL;
 			return NULL;
-		dev = my_stat.st_rdev;
+		dev.dev = my_stat.st_rdev;
 	}
 	}
 #else
 #else
 	{
 	{
 		char *p = strstr(devpath, "COM"), *p2;
 		char *p = strstr(devpath, "COM"), *p2;
 		if (!p)
 		if (!p)
 			return NULL;
 			return NULL;
-		dev = strtol(&p[3], &p2, 10);
+		dev.com = strtol(&p[3], &p2, 10);
 		if (p2 == p)
 		if (p2 == p)
 			return NULL;
 			return NULL;
 	}
 	}
 #endif
 #endif
+	
+	return bfg_claim_any(api, (verbose ? devpath : NULL), &dev);
+}
 
 
-	HASH_FIND(hh, claims, &dev, sizeof(dev), c);
-	if (c)
-		return c->drv;
-
-	if (!api)
-		return NULL;
-
-	c = malloc(sizeof(*c));
-	c->dev = dev;
-	c->drv = api;
-	HASH_ADD(hh, claims, dev, sizeof(dev), c);
-	return NULL;
+struct device_drv *bfg_claim_usb(struct device_drv * const api, const bool verbose, const uint8_t usbbus, const uint8_t usbaddr)
+{
+	const my_dev_t dev = {
+		.bus = BDB_USB,
+		.usbbus = usbbus,
+		.usbaddr = usbaddr,
+	};
+	char *desc = NULL;
+	
+	if (verbose)
+	{
+		desc = alloca(3 + 1 + 3 + 1);
+		sprintf(desc, "%03u:%03u", (unsigned)usbbus, (unsigned)usbaddr);
+	}
+	
+	return bfg_claim_any(api, desc, &dev);
 }
 }
 
 
 // This code is purely for debugging but is very useful for that
 // This code is purely for debugging but is very useful for that

+ 7 - 1
fpgautils.h

@@ -25,10 +25,16 @@ extern int _serial_detect(struct device_drv *api, detectone_func_t, autoscan_fun
 	_serial_detect(api, detectone,     NULL, 2)
 	_serial_detect(api, detectone,     NULL, 2)
 #define noserial_detect(api, autoscan)  \
 #define noserial_detect(api, autoscan)  \
 	_serial_detect(api, NULL     , autoscan, 0)
 	_serial_detect(api, NULL     , autoscan, 0)
+#define noserial_detect_manual(api, autoscan)  \
+	_serial_detect(api, NULL     , autoscan, 4)
 extern int _serial_autodetect(detectone_func_t, ...);
 extern int _serial_autodetect(detectone_func_t, ...);
 #define serial_autodetect(...)  _serial_autodetect(__VA_ARGS__, NULL)
 #define serial_autodetect(...)  _serial_autodetect(__VA_ARGS__, NULL)
 
 
-extern struct device_drv *serial_claim(const char *devpath, struct device_drv *);
+extern struct device_drv *bfg_claim_serial(struct device_drv * const, const bool verbose, const char * const devpath);
+#define serial_claim(devpath, drv)    bfg_claim_serial(drv, false, devpath)
+#define serial_claim_v(devpath, drv)  bfg_claim_serial(drv, true , devpath)
+extern struct device_drv *bfg_claim_usb(struct device_drv * const, const bool verbose, const uint8_t usbbus, const uint8_t usbaddr);
+#define bfg_claim_libusb(api, verbose, dev)  bfg_claim_usb(api, verbose, libusb_get_bus_number(dev), libusb_get_device_address(dev))
 
 
 extern int serial_open(const char *devpath, unsigned long baud, uint8_t timeout, bool purge);
 extern int serial_open(const char *devpath, unsigned long baud, uint8_t timeout, bool purge);
 extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol);
 extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol);

+ 10 - 0
ft232r.c

@@ -63,6 +63,7 @@ void ft232r_scan()
 	struct ft232r_device_info *info;
 	struct ft232r_device_info *info;
 	int err;
 	int err;
 	unsigned char buf[0x100];
 	unsigned char buf[0x100];
+	int skipped = 0;
 
 
 	ft232r_scan_free();
 	ft232r_scan_free();
 
 
@@ -76,6 +77,12 @@ void ft232r_scan()
 	ft232r_devinfo_list = malloc(sizeof(struct ft232r_device_info *) * (count + 1));
 	ft232r_devinfo_list = malloc(sizeof(struct ft232r_device_info *) * (count + 1));
 
 
 	for (i = 0; i < count; ++i) {
 	for (i = 0; i < count; ++i) {
+		if (bfg_claim_libusb(NULL, false, list[i]))
+		{
+			++skipped;
+			continue;
+		}
+		
 		err = libusb_get_device_descriptor(list[i], &desc);
 		err = libusb_get_device_descriptor(list[i], &desc);
 		if (unlikely(err)) {
 		if (unlikely(err)) {
 			applog(LOG_ERR, "ft232r_scan: Error getting device descriptor: %s", bfg_strerror(err, BST_LIBUSB));
 			applog(LOG_ERR, "ft232r_scan: Error getting device descriptor: %s", bfg_strerror(err, BST_LIBUSB));
@@ -118,6 +125,9 @@ void ft232r_scan()
 
 
 	ft232r_devinfo_list[found] = NULL;
 	ft232r_devinfo_list[found] = NULL;
 	libusb_free_device_list(list, 1);
 	libusb_free_device_list(list, 1);
+	
+	if (skipped)
+		applog(LOG_DEBUG, "%s: Skipping probe of %d claimed devices", __func__, skipped);
 }
 }
 
 
 int ft232r_detect(const char *product_needle, const char *serial, foundusb_func_t cb)
 int ft232r_detect(const char *product_needle, const char *serial, foundusb_func_t cb)

+ 11 - 0
libztex.c

@@ -738,6 +738,7 @@ int libztex_scanDevices(struct libztex_dev_list*** devs_p)
 	int found, max_found = 0, pos = 0, err, rescan, ret = 0;
 	int found, max_found = 0, pos = 0, err, rescan, ret = 0;
 	libusb_device **list = NULL;
 	libusb_device **list = NULL;
 	ssize_t cnt, i;
 	ssize_t cnt, i;
+	int skipped = 0;
 
 
 	do {
 	do {
 		cnt = libusb_get_device_list(NULL, &list);
 		cnt = libusb_get_device_list(NULL, &list);
@@ -747,6 +748,12 @@ int libztex_scanDevices(struct libztex_dev_list*** devs_p)
 		}
 		}
 
 
 		for (found = rescan = i = 0; i < cnt; i++) {
 		for (found = rescan = i = 0; i < cnt; i++) {
+			if (bfg_claim_libusb(NULL, false, list[i]))
+			{
+				++skipped;
+				continue;
+			}
+			
 			err = libztex_checkDevice(list[i]);
 			err = libztex_checkDevice(list[i]);
 			switch (err) {
 			switch (err) {
 			case CHECK_ERROR:
 			case CHECK_ERROR:
@@ -828,6 +835,10 @@ done:
 		free(devs);
 		free(devs);
 	if (list)
 	if (list)
 		libusb_free_device_list(list, 1);
 		libusb_free_device_list(list, 1);
+	
+	if (skipped)
+		applog(LOG_DEBUG, "%s: Skipping probe of %d claimed devices", __func__, skipped);
+	
 	return ret;
 	return ret;
 }
 }
 
 

+ 278 - 142
miner.c

@@ -162,6 +162,8 @@ static bool opt_display_devs;
 static bool opt_removedisabled;
 static bool opt_removedisabled;
 int total_devices;
 int total_devices;
 struct cgpu_info **devices;
 struct cgpu_info **devices;
+int total_devices_new;
+struct cgpu_info **devices_new;
 bool have_opencl;
 bool have_opencl;
 int opt_n_threads = -1;
 int opt_n_threads = -1;
 int mining_threads;
 int mining_threads;
@@ -1067,9 +1069,42 @@ static int temp_strtok(char *base, char **n)
 	return atoi(i);
 	return atoi(i);
 }
 }
 
 
+static void load_temp_config_cgpu(struct cgpu_info *cgpu, char **cutoff_np, char **target_np)
+{
+	int target_off, val;
+	
+	// cutoff default may be specified by driver during probe; otherwise, opt_cutofftemp (const)
+	if (!cgpu->cutofftemp)
+		cgpu->cutofftemp = opt_cutofftemp;
+	
+	// target default may be specified by driver, and is moved with offset; otherwise, offset minus 6
+	if (cgpu->targettemp)
+		target_off = cgpu->targettemp - cgpu->cutofftemp;
+	else
+		target_off = -6;
+	
+	val = temp_strtok(temp_cutoff_str, cutoff_np);
+	if (val < 0 || val > 200)
+		quit(1, "Invalid value passed to set temp cutoff");
+	if (val)
+		cgpu->cutofftemp = val;
+	
+	val = temp_strtok(temp_target_str, target_np);
+	if (val < 0 || val > 200)
+		quit(1, "Invalid value passed to set temp target");
+	if (val)
+		cgpu->targettemp = val;
+	else
+		cgpu->targettemp = cgpu->cutofftemp + target_off;
+	
+	applog(LOG_DEBUG, "%"PRIprepr": Set temperature config: target=%d cutoff=%d",
+	       cgpu->proc_repr,
+	       cgpu->targettemp, cgpu->cutofftemp);
+}
+
 static void load_temp_config()
 static void load_temp_config()
 {
 {
-	int i, val = 0, target_off;
+	int i;
 	char *cutoff_n, *target_n;
 	char *cutoff_n, *target_n;
 	struct cgpu_info *cgpu;
 	struct cgpu_info *cgpu;
 
 
@@ -1078,34 +1113,7 @@ static void load_temp_config()
 
 
 	for (i = 0; i < total_devices; ++i) {
 	for (i = 0; i < total_devices; ++i) {
 		cgpu = get_devices(i);
 		cgpu = get_devices(i);
-		
-		// cutoff default may be specified by driver during probe; otherwise, opt_cutofftemp (const)
-		if (!cgpu->cutofftemp)
-			cgpu->cutofftemp = opt_cutofftemp;
-		
-		// target default may be specified by driver, and is moved with offset; otherwise, offset minus 6
-		if (cgpu->targettemp)
-			target_off = cgpu->targettemp - cgpu->cutofftemp;
-		else
-			target_off = -6;
-		
-		val = temp_strtok(temp_cutoff_str, &cutoff_n);
-		if (val < 0 || val > 200)
-			quit(1, "Invalid value passed to set temp cutoff");
-		if (val)
-			cgpu->cutofftemp = val;
-		
-		val = temp_strtok(temp_target_str, &target_n);
-		if (val < 0 || val > 200)
-			quit(1, "Invalid value passed to set temp target");
-		if (val)
-			cgpu->targettemp = val;
-		else
-			cgpu->targettemp = cgpu->cutofftemp + target_off;
-		
-		applog(LOG_DEBUG, "%"PRIprepr": Set temperature config: target=%d cutoff=%d",
-		       cgpu->proc_repr,
-		       cgpu->targettemp, cgpu->cutofftemp);
+		load_temp_config_cgpu(cgpu, &cutoff_n, &target_n);
 	}
 	}
 	if (cutoff_n != temp_cutoff_str && cutoff_n[0])
 	if (cutoff_n != temp_cutoff_str && cutoff_n[0])
 		quit(1, "Too many values passed to set temp cutoff");
 		quit(1, "Too many values passed to set temp cutoff");
@@ -8554,6 +8562,239 @@ extern void setup_pthread_cancel_workaround();
 extern struct sigaction pcwm_orig_term_handler;
 extern struct sigaction pcwm_orig_term_handler;
 #endif
 #endif
 
 
+static
+void drv_detect_all()
+{
+#ifdef USE_X6500
+	if (likely(have_libusb))
+		ft232r_scan();
+#endif
+
+#ifdef HAVE_OPENCL
+	if (!opt_nogpu)
+		opencl_api.drv_detect();
+	gpu_threads = 0;
+#endif
+
+#ifdef USE_ICARUS
+	if (!opt_scrypt)
+	{
+		cairnsmore_drv.drv_detect();
+		icarus_drv.drv_detect();
+	}
+#endif
+
+#ifdef USE_BITFORCE
+	if (!opt_scrypt)
+		bitforce_drv.drv_detect();
+#endif
+
+#ifdef USE_MODMINER
+	if (!opt_scrypt)
+		modminer_drv.drv_detect();
+#endif
+
+#ifdef USE_X6500
+	if (likely(have_libusb) && !opt_scrypt)
+		x6500_api.drv_detect();
+#endif
+
+#ifdef USE_ZTEX
+	if (likely(have_libusb) && !opt_scrypt)
+		ztex_drv.drv_detect();
+#endif
+
+	/* Detect avalon last since it will try to claim the device regardless
+	 * as detection is unreliable. */
+#ifdef USE_AVALON
+	if (!opt_scrypt)
+		avalon_drv.drv_detect();
+#endif
+
+#ifdef WANT_CPUMINE
+	cpu_drv.drv_detect();
+#endif
+
+#ifdef USE_X6500
+	if (likely(have_libusb))
+		ft232r_scan_free();
+#endif
+}
+
+static
+void allocate_cgpu(struct cgpu_info *cgpu, unsigned int *kp)
+{
+	struct thr_info *thr;
+	int j;
+	
+	struct device_drv *api = cgpu->drv;
+	if (!cgpu->devtype)
+		cgpu->devtype = "PGA";
+	cgpu->cgminer_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET;
+	
+	int threadobj = cgpu->threads;
+	if (!threadobj)
+		// Create a fake thread object to handle hashmeter etc
+		threadobj = 1;
+	cgpu->thr = calloc(threadobj + 1, sizeof(*cgpu->thr));
+	cgpu->thr[threadobj] = NULL;
+	cgpu->status = LIFE_INIT;
+
+	cgpu->max_hashes = 0;
+
+	// Setup thread structs before starting any of the threads, in case they try to interact
+	for (j = 0; j < threadobj; ++j, ++*kp) {
+		thr = get_thread(*kp);
+		thr->id = *kp;
+		thr->cgpu = cgpu;
+		thr->device_thread = j;
+		thr->work_restart_notifier[1] = INVSOCK;
+		thr->mutex_request[1] = INVSOCK;
+		thr->_job_transition_in_progress = true;
+		timerclear(&thr->tv_morework);
+		thr->_last_sbr_state = true;
+
+		thr->scanhash_working = true;
+		thr->hashes_done = 0;
+		timerclear(&thr->tv_hashes_done);
+		cgtime(&thr->tv_lastupdate);
+		thr->tv_poll.tv_sec = -1;
+		thr->_max_nonce = api->can_limit_work ? api->can_limit_work(thr) : 0xffffffff;
+
+		cgpu->thr[j] = thr;
+	}
+	
+	if (!cgpu->threads)
+		memcpy(&cgpu->thr[0]->notifier, &cgpu->device->thr[0]->notifier, sizeof(cgpu->thr[0]->notifier));
+	else
+	for (j = 0; j < cgpu->threads; ++j)
+	{
+		thr = cgpu->thr[j];
+		notifier_init(thr->notifier);
+	}
+}
+
+static
+void start_cgpu(struct cgpu_info *cgpu)
+{
+	struct thr_info *thr;
+	int j;
+	
+	for (j = 0; j < cgpu->threads; ++j) {
+		thr = cgpu->thr[j];
+
+		/* Enable threads for devices set not to mine but disable
+		 * their queue in case we wish to enable them later */
+		if (cgpu->drv->thread_prepare && !cgpu->drv->thread_prepare(thr))
+			continue;
+
+		thread_reportout(thr);
+
+		if (unlikely(thr_info_create(thr, NULL, miner_thread, thr)))
+			quit(1, "thread %d create failed", thr->id);
+	}
+	if (cgpu->deven == DEV_ENABLED)
+		proc_enable(cgpu);
+}
+
+int scan_serial(const char *s)
+{
+	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+	struct string_elist *orig_scan_devices;
+	int devcount, i, mining_threads_new = 0;
+	unsigned int k;
+	struct string_elist *iter, *tmp;
+	struct cgpu_info *cgpu;
+	struct thr_info *thr;
+	void *p;
+	char *dummy = "\0";
+	
+	mutex_lock(&mutex);
+	orig_scan_devices = scan_devices;
+	devcount = total_devices;
+	
+	if (s)
+	{
+		// Make temporary scan_devices list
+		scan_devices = NULL;
+		string_elist_add("noauto", &scan_devices);
+		string_elist_add(s, &scan_devices);
+	}
+	
+	drv_detect_all();
+	
+	wr_lock(&devices_lock);
+	p = realloc(devices, sizeof(struct cgpu_info *) * (total_devices + total_devices_new + 1));
+	if (unlikely(!p))
+	{
+		wr_unlock(&devices_lock);
+		applog(LOG_ERR, "scan_serial: realloc failed trying to grow devices array");
+		goto out;
+	}
+	devices = p;
+	wr_unlock(&devices_lock);
+	
+	for (i = 0; i < total_devices_new; ++i)
+	{
+		cgpu = devices_new[i];
+		mining_threads_new += cgpu->threads ?: 1;
+	}
+	
+	wr_lock(&mining_thr_lock);
+	mining_threads_new += mining_threads;
+	p = realloc(mining_thr, sizeof(struct thr_info *) * mining_threads_new);
+	if (unlikely(!p))
+	{
+		wr_unlock(&mining_thr_lock);
+		applog(LOG_ERR, "scan_serial: realloc failed trying to grow mining_thr");
+		goto out;
+	}
+	mining_thr = p;
+	wr_unlock(&mining_thr_lock);
+	for (i = mining_threads; i < mining_threads_new; ++i) {
+		mining_thr[i] = calloc(1, sizeof(*thr));
+		if (!mining_thr[i])
+		{
+			applog(LOG_ERR, "scan_serial: Failed to calloc mining_thr[%d]", i);
+			for ( ; --i >= mining_threads; )
+				free(mining_thr[i]);
+			goto out;
+		}
+	}
+	
+	k = mining_threads;
+	for (i = 0; i < total_devices_new; ++i)
+	{
+		cgpu = devices_new[i];
+		load_temp_config_cgpu(cgpu, &dummy, &dummy);
+		allocate_cgpu(cgpu, &k);
+		start_cgpu(cgpu);
+		register_device(cgpu);
+		++total_devices;
+	}
+	
+#ifdef HAVE_CURSES
+	switch_logsize();
+#endif
+	
+out:
+	if (s)
+	{
+		DL_FOREACH_SAFE(scan_devices, iter, tmp)
+		{
+			string_elist_del(&scan_devices, iter);
+		}
+		scan_devices = orig_scan_devices;
+	}
+	
+	total_devices_new = 0;
+	
+	devcount = total_devices - devcount;
+	mutex_unlock(&mutex);
+	
+	return devcount;
+}
+
 static void probe_pools(void)
 static void probe_pools(void)
 {
 {
 	int i;
 	int i;
@@ -8600,7 +8841,7 @@ int main(int argc, char *argv[])
 	struct thr_info *thr;
 	struct thr_info *thr;
 	struct block *block;
 	struct block *block;
 	unsigned int k;
 	unsigned int k;
-	int i, j;
+	int i;
 	char *s;
 	char *s;
 
 
 #ifdef WIN32
 #ifdef WIN32
@@ -8749,11 +8990,6 @@ int main(int argc, char *argv[])
 		successful_connect = true;
 		successful_connect = true;
 	}
 	}
 
 
-#ifdef USE_X6500
-	if (likely(have_libusb))
-		ft232r_scan();
-#endif
-
 #ifdef HAVE_CURSES
 #ifdef HAVE_CURSES
 	if (opt_realquiet || opt_display_devs)
 	if (opt_realquiet || opt_display_devs)
 		use_curses = false;
 		use_curses = false;
@@ -8832,59 +9068,11 @@ int main(int argc, char *argv[])
 	}
 	}
 #endif
 #endif
 
 
-#ifdef HAVE_OPENCL
-	if (!opt_nogpu)
-		opencl_api.drv_detect();
-	gpu_threads = 0;
-#endif
-
-#ifdef USE_ICARUS
-	if (!opt_scrypt)
-	{
-		cairnsmore_drv.drv_detect();
-		icarus_drv.drv_detect();
-	}
-#endif
-
-#ifdef USE_BITFORCE
-	if (!opt_scrypt)
-		bitforce_drv.drv_detect();
-#endif
-
-#ifdef USE_MODMINER
-	if (!opt_scrypt)
-		modminer_drv.drv_detect();
-#endif
-
-#ifdef USE_X6500
-	if (likely(have_libusb) && !opt_scrypt)
-		x6500_api.drv_detect();
-#endif
-
-#ifdef USE_ZTEX
-	if (likely(have_libusb) && !opt_scrypt)
-		ztex_drv.drv_detect();
-#endif
-
-	/* Detect avalon last since it will try to claim the device regardless
-	 * as detection is unreliable. */
-#ifdef USE_AVALON
-	if (!opt_scrypt)
-		avalon_drv.drv_detect();
-#endif
-
-#ifdef WANT_CPUMINE
-	cpu_drv.drv_detect();
-#endif
-
-#ifdef USE_X6500
-	if (likely(have_libusb))
-		ft232r_scan_free();
-#endif
-
-	for (i = 0; i < total_devices; ++i)
-		if (!devices[i]->devtype)
-			devices[i]->devtype = "PGA";
+	drv_detect_all();
+	total_devices = total_devices_new;
+	devices = devices_new;
+	total_devices_new = 0;
+	devices_new = NULL;
 
 
 	if (opt_display_devs) {
 	if (opt_display_devs) {
 		applog(LOG_ERR, "Devices detected:");
 		applog(LOG_ERR, "Devices detected:");
@@ -8933,9 +9121,6 @@ int main(int argc, char *argv[])
 
 
 	load_temp_config();
 	load_temp_config();
 
 
-	for (i = 0; i < total_devices; ++i)
-		devices[i]->cgminer_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET;
-
 #ifdef HAVE_CURSES
 #ifdef HAVE_CURSES
 	switch_logsize();
 	switch_logsize();
 #endif
 #endif
@@ -9091,62 +9276,13 @@ begin_bench:
 	k = 0;
 	k = 0;
 	for (i = 0; i < total_devices; ++i) {
 	for (i = 0; i < total_devices; ++i) {
 		struct cgpu_info *cgpu = devices[i];
 		struct cgpu_info *cgpu = devices[i];
-		struct device_drv *api = cgpu->drv;
-		int threadobj = cgpu->threads;
-		if (!threadobj)
-			// Create a fake thread object to handle hashmeter etc
-			threadobj = 1;
-		cgpu->thr = calloc(threadobj + 1, sizeof(*cgpu->thr));
-		cgpu->thr[threadobj] = NULL;
-		cgpu->status = LIFE_INIT;
-
-		cgpu->max_hashes = 0;
-
-		// Setup thread structs before starting any of the threads, in case they try to interact
-		for (j = 0; j < threadobj; ++j, ++k) {
-			thr = get_thread(k);
-			thr->id = k;
-			thr->cgpu = cgpu;
-			thr->device_thread = j;
-			thr->work_restart_notifier[1] = INVSOCK;
-			thr->mutex_request[1] = INVSOCK;
-			thr->_job_transition_in_progress = true;
-			timerclear(&thr->tv_morework);
-			thr->_last_sbr_state = true;
-
-			thr->scanhash_working = true;
-			thr->hashes_done = 0;
-			timerclear(&thr->tv_hashes_done);
-			cgtime(&thr->tv_lastupdate);
-			thr->tv_poll.tv_sec = -1;
-			thr->_max_nonce = api->can_limit_work ? api->can_limit_work(thr) : 0xffffffff;
-
-			cgpu->thr[j] = thr;
-		}
+		allocate_cgpu(cgpu, &k);
 	}
 	}
 
 
 	// Start threads
 	// Start threads
 	for (i = 0; i < total_devices; ++i) {
 	for (i = 0; i < total_devices; ++i) {
 		struct cgpu_info *cgpu = devices[i];
 		struct cgpu_info *cgpu = devices[i];
-		if (!cgpu->threads)
-			memcpy(&cgpu->thr[0]->notifier, &cgpu->device->thr[0]->notifier, sizeof(cgpu->thr[0]->notifier));
-		for (j = 0; j < cgpu->threads; ++j) {
-			thr = cgpu->thr[j];
-
-			notifier_init(thr->notifier);
-
-			/* Enable threads for devices set not to mine but disable
-			 * their queue in case we wish to enable them later */
-			if (cgpu->drv->thread_prepare && !cgpu->drv->thread_prepare(thr))
-				continue;
-
-			thread_reportout(thr);
-
-			if (unlikely(thr_info_create(thr, NULL, miner_thread, thr)))
-				quit(1, "thread %d create failed", thr->id);
-		}
-		if (cgpu->deven == DEV_ENABLED)
-			proc_enable(cgpu);
+		start_cgpu(cgpu);
 	}
 	}
 
 
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL

+ 3 - 0
miner.h

@@ -970,6 +970,8 @@ extern int mining_threads;
 extern struct cgpu_info *cpus;
 extern struct cgpu_info *cpus;
 extern int total_devices;
 extern int total_devices;
 extern struct cgpu_info **devices;
 extern struct cgpu_info **devices;
+extern int total_devices_new;
+extern struct cgpu_info **devices_new;
 extern int total_pools;
 extern int total_pools;
 extern struct pool **pools;
 extern struct pool **pools;
 extern const char *algo_names[];
 extern const char *algo_names[];
@@ -1296,6 +1298,7 @@ extern void __copy_work(struct work *work, const struct work *base_work);
 extern struct work *copy_work(const struct work *base_work);
 extern struct work *copy_work(const struct work *base_work);
 extern struct thr_info *get_thread(int thr_id);
 extern struct thr_info *get_thread(int thr_id);
 extern struct cgpu_info *get_devices(int id);
 extern struct cgpu_info *get_devices(int id);
+extern int scan_serial(const char *);
 
 
 enum api_data_type {
 enum api_data_type {
 	API_ESCAPE,
 	API_ESCAPE,

+ 8 - 0
util.h

@@ -189,4 +189,12 @@ struct timeval *select_timeout(struct timeval *tvp_timeout, struct timeval *tvp_
 }
 }
 
 
 
 
+#define RUNONCE(rv)  do {  \
+	static bool _runonce = false;  \
+	if (_runonce)  \
+		return rv;  \
+	_runonce = true;  \
+} while(0)
+
+
 #endif /* __UTIL_H__ */
 #endif /* __UTIL_H__ */