Browse Source

Merge branch 'uniscan' into bfgminer

Luke Dashjr 12 years ago
parent
commit
a12f32f21c
35 changed files with 725 additions and 732 deletions
  1. 4 0
      Makefile.am
  2. 20 53
      api.c
  3. 2 0
      configure.ac
  4. 50 0
      deviceapi.c
  5. 29 0
      deviceapi.h
  6. 2 1
      driver-avalon.c
  7. 1 1
      driver-bfsb.c
  8. 1 1
      driver-bigpic.c
  9. 2 2
      driver-bitforce.c
  10. 1 1
      driver-bitfury.c
  11. 3 6
      driver-cairnsmore.c
  12. 3 0
      driver-cpu.c
  13. 7 8
      driver-erupter.c
  14. 2 1
      driver-icarus.c
  15. 1 1
      driver-knc.c
  16. 1 1
      driver-littlefury.c
  17. 1 1
      driver-metabank.c
  18. 1 1
      driver-modminer.c
  19. 2 2
      driver-nanofury.c
  20. 2 1
      driver-opencl.c
  21. 1 1
      driver-proxy.c
  22. 5 4
      driver-x6500.c
  23. 53 29
      driver-ztex.c
  24. 179 178
      fpgautils.c
  25. 2 0
      fpgautils.h
  26. 19 72
      ft232r.c
  27. 26 144
      libztex.c
  28. 10 1
      libztex.h
  29. 135 0
      lowl-usb.c
  30. 77 13
      lowlevel.c
  31. 20 4
      lowlevel.h
  32. 4 0
      mcp2210.c
  33. 45 204
      miner.c
  34. 9 1
      miner.h
  35. 5 0
      util.h

+ 4 - 0
Makefile.am

@@ -180,6 +180,10 @@ bfgminer_SOURCES += iospeeds.h iospeeds_posix.h
 endif
 endif
 
+if HAVE_LIBUSB
+bfgminer_SOURCES += lowl-usb.c
+endif
+
 if NEED_BFG_LOWLEVEL
 bfgminer_SOURCES += lowlevel.c lowlevel.h
 endif

+ 20 - 53
api.c

@@ -26,6 +26,7 @@
 #include <sys/types.h>
 
 #include "compat.h"
+#include "deviceapi.h"
 #ifdef USE_LIBMICROHTTPD
 #include "httpsrv.h"
 #endif
@@ -86,57 +87,6 @@ static const char *SCRYPTSTR = "scrypt";
 #endif
 static const char *SHA256STR = "sha256";
 
-static const char *DEVICECODE = ""
-#ifdef HAVE_OPENCL
-			"GPU "
-#endif
-#ifdef USE_BITFORCE
-			"BFL "
-#endif
-#ifdef USE_BITFURY
-			"BFY "
-#endif
-#ifdef USE_BIGPIC
-			"BPM "
-#endif
-#ifdef USE_BFSB
-			"BSB "
-#endif
-#ifdef USE_ICARUS
-			"ICA "
-#endif
-#ifdef USE_LITTLEFURY
-			"LFY "
-#endif
-#ifdef USE_METABANK
-			"MBF "
-#endif
-#ifdef USE_NANOFURY
-			"NFY "
-#endif
-#ifdef USE_AVALON
-			"AVA "
-#endif
-#ifdef USE_LIBMICROHTTPD
-			"SGW "
-#endif
-#ifdef USE_LIBEVENT
-			"SSM "
-#endif
-#ifdef USE_X6500
-			"XBS "
-#endif
-#ifdef USE_ZTEX
-			"ZTX "
-#endif
-#ifdef USE_MODMINER
-			"MMQ "
-#endif
-#ifdef WANT_CPUMINE
-			"CPU "
-#endif
-			"";
-
 static const char *OSINFO =
 #if defined(__linux)
 			"Linux";
@@ -1350,6 +1300,7 @@ static void minerconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __
 	struct api_data *root = NULL;
 	char buf[TMPBUFSIZ];
 	bool io_open;
+	struct driver_registration *reg, *regtmp;
 	int gpucount = 0;
 	int pgacount = 0;
 	int cpucount = 0;
@@ -1391,7 +1342,23 @@ static void minerconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __
 	root = api_add_string(root, "ADL in use", adlinuse, false);
 	root = api_add_const(root, "Strategy", strategies[pool_strategy].s, false);
 	root = api_add_int(root, "Log Interval", &opt_log_interval, false);
-	root = api_add_const(root, "Device Code", DEVICECODE, false);
+	
+	strcpy(buf, ""
+#ifdef USE_LIBMICROHTTPD
+			" SGW"
+#endif
+#ifdef USE_LIBEVENT
+			" SSM"
+#endif
+	);
+
+	BFG_FOREACH_DRIVER_BY_DNAME(reg, regtmp)
+	{
+		const struct device_drv * const drv = reg->drv;
+		tailsprintf(buf, sizeof(buf), " %s", drv->name);
+	}
+	root = api_add_const(root, "Device Code", &buf[1], true);
+	
 	root = api_add_const(root, "OS", OSINFO, false);
 	root = api_add_bool(root, "Failover-Only", &opt_fail_only, false);
 	root = api_add_int(root, "ScanTime", &opt_scantime, false);
@@ -2115,7 +2082,7 @@ static void gpuenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 {
 	int id;
 
-	if (gpu_threads == 0) {
+	if (!nDevs) {
 		message(io_data, MSG_GPUNON, 0, NULL, isjson);
 		return;
 	}

+ 2 - 0
configure.ac

@@ -713,6 +713,7 @@ fi
 
 if test x$need_fpgautils = xyes; then
 	AC_DEFINE([HAVE_FPGAUTILS], [1], [Defined to 1 if fpgautils is being used])
+	need_lowlevel=yes
 	
 	if $have_win32; then
 		echo '#include <iospeeds.h>' >iospeeds_local.h
@@ -914,6 +915,7 @@ AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([HAVE_SENSORS], [test x$with_sensors = xyes])
 AM_CONDITIONAL([HAVE_CYGWIN], [test x$have_cygwin = xtrue])
+AM_CONDITIONAL([HAVE_LIBUSB], [test x$libusb = xyes])
 AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue])
 AM_CONDITIONAL([HAVE_x86_64], [test x$have_x86_64 = xtrue])
 AM_CONDITIONAL([HAVE_WIN_DDKUSB], [test x$found_ddkusb = xtrue])

+ 50 - 0
deviceapi.c

@@ -31,6 +31,56 @@
 #include "miner.h"
 #include "util.h"
 
+struct driver_registration *_bfg_drvreg1;
+struct driver_registration *_bfg_drvreg2;
+
+void _bfg_register_driver(const struct device_drv *drv)
+{
+	static struct driver_registration *initlist;
+	struct driver_registration *ndr;
+	
+	if (!drv)
+	{
+		// Move initlist to hashtables
+		LL_FOREACH(initlist, ndr)
+		{
+			drv = ndr->drv;
+			if (drv->drv_init)
+				drv->drv_init();
+			HASH_ADD_KEYPTR(hh , _bfg_drvreg1, drv->dname, strlen(drv->dname), ndr);
+			HASH_ADD_KEYPTR(hh2, _bfg_drvreg2, drv->name , strlen(drv->name ), ndr);
+		}
+		initlist = NULL;
+		return;
+	}
+	
+	ndr = malloc(sizeof(*ndr));
+	*ndr = (struct driver_registration){
+		.drv = drv,
+	};
+	LL_PREPEND(initlist, ndr);
+}
+
+static
+int sort_drv_by_dname(struct driver_registration * const a, struct driver_registration * const b)
+{
+	return strcmp(a->drv->dname, b->drv->dname);
+};
+
+static
+int sort_drv_by_priority(struct driver_registration * const a, struct driver_registration * const b)
+{
+	return a->drv->probe_priority - b->drv->probe_priority;
+};
+
+void bfg_devapi_init()
+{
+	_bfg_register_driver(NULL);
+	HASH_SRT(hh , _bfg_drvreg1, sort_drv_by_dname   );
+	HASH_SRT(hh2, _bfg_drvreg2, sort_drv_by_priority);
+}
+
+
 bool hashes_done(struct thr_info *thr, int64_t hashes, struct timeval *tvp_hashes, uint32_t *max_nonce)
 {
 	struct cgpu_info *cgpu = thr->cgpu;

+ 29 - 0
deviceapi.h

@@ -7,6 +7,35 @@
 
 #include "miner.h"
 
+struct driver_registration;
+struct driver_registration {
+	const struct device_drv *drv;
+	
+	UT_hash_handle hh;   // hash & order by dname
+	UT_hash_handle hh2;  // hash by name, order by priority
+	struct driver_registration *next;  // DO NOT USE
+};
+
+extern struct driver_registration *_bfg_drvreg1;
+extern struct driver_registration *_bfg_drvreg2;
+extern void bfg_devapi_init();
+
+#define BFG_FOREACH_DRIVER_BY_DNAME(reg, tmp)  \
+	HASH_ITER(hh , _bfg_drvreg1, reg, tmp)
+#define BFG_FOREACH_DRIVER_BY_PRIORITY(reg, tmp)  \
+	HASH_ITER(hh2, _bfg_drvreg2, reg, tmp)
+
+extern void _bfg_register_driver(const struct device_drv *);
+#define BFG_REGISTER_DRIVER(drv)                \
+	struct device_drv drv;                      \
+	__attribute__((constructor))                \
+	static void __bfg_register_drv_ ## drv() {  \
+		_bfg_register_driver(&drv);             \
+	}                                           \
+// END BFG_REGISTER_DRIVER
+
+extern bool bfg_need_detect_rescan;
+
 extern void request_work(struct thr_info *);
 extern struct work *get_work(struct thr_info *);
 extern bool hashes_done(struct thr_info *, int64_t hashes, struct timeval *tvp_hashes, uint32_t *max_nonce);

+ 2 - 1
driver-avalon.c

@@ -42,8 +42,9 @@
 #include "logging.h"
 #include "util.h"
 
+BFG_REGISTER_DRIVER(avalon_drv)
+
 static int option_offset = -1;
-struct device_drv avalon_drv;
 
 static int avalon_init_task(struct avalon_task *at,
 			    uint8_t reset, uint8_t ff, uint8_t fan,

+ 1 - 1
driver-bfsb.c

@@ -31,7 +31,7 @@
 #include "spidevc.h"
 #include "driver-bitfury.h"
 
-struct device_drv bfsb_drv;
+BFG_REGISTER_DRIVER(bfsb_drv)
 
 static
 bool bfsb_spi_txrx(struct spi_port *port)

+ 1 - 1
driver-bigpic.c

@@ -29,7 +29,7 @@
 
 #include <stdio.h>
 
-struct device_drv bigpic_drv;
+BFG_REGISTER_DRIVER(bigpic_drv)
 
 //------------------------------------------------------------------------------
 static bool bigpic_detect_custom(const char *devpath, struct device_drv *api, struct bigpic_info *info)

+ 2 - 2
driver-bitforce.c

@@ -61,8 +61,8 @@ static const char *protonames[] = {
 	"parallel queue",
 };
 
-struct device_drv bitforce_drv;
-struct device_drv bitforce_queue_api;
+BFG_REGISTER_DRIVER(bitforce_drv)
+BFG_REGISTER_DRIVER(bitforce_queue_api)
 
 // Code must deal with a timeout
 #define BFopen(devpath)  serial_open(devpath, 0, 250, true)

+ 1 - 1
driver-bitfury.c

@@ -37,7 +37,7 @@
 #include "util.h"
 #include "spidevc.h"
 
-struct device_drv bitfury_drv;
+BFG_REGISTER_DRIVER(bitfury_drv)
 
 static
 int bitfury_autodetect()

+ 3 - 6
driver-cairnsmore.c

@@ -27,9 +27,7 @@
 #define CAIRNSMORE1_DEFAULT_CLOCK  200
 #define CAIRNSMORE1_MAXIMUM_CLOCK  210
 
-struct device_drv cairnsmore_drv;
-
-static void cairnsmore_drv_init();
+BFG_REGISTER_DRIVER(cairnsmore_drv)
 
 static bool cairnsmore_detect_one(const char *devpath)
 {
@@ -59,7 +57,6 @@ static int cairnsmore_detect_auto(void)
 
 static void cairnsmore_detect()
 {
-	cairnsmore_drv_init();
 	// Actual serial detection is handled by Icarus driver
 	serial_detect_auto_byname(&cairnsmore_drv, cairnsmore_detect_one, cairnsmore_detect_auto);
 }
@@ -211,9 +208,9 @@ static void cairnsmore_drv_init()
 	cairnsmore_drv.thread_init = cairnsmore_init;
 	cairnsmore_drv.identify_device = cairnsmore_identify;
 	cairnsmore_drv.get_api_extra_device_status = cairnsmore_drv_extra_device_status;
+	++cairnsmore_drv.probe_priority;
 }
 
 struct device_drv cairnsmore_drv = {
-	// Needed to get to cairnsmore_drv_init at all
-	.drv_detect = cairnsmore_detect,
+	.drv_init = cairnsmore_drv_init,
 };

+ 3 - 0
driver-cpu.c

@@ -42,6 +42,8 @@
 	#include <fcntl.h>
 #endif
 
+BFG_REGISTER_DRIVER(cpu_drv)
+
 #if defined(__linux) && defined(CPU_ZERO)  /* Linux specific policy and affinity management */
 #include <sched.h>
 static inline void drop_policy(void)
@@ -877,6 +879,7 @@ CPUSearch:
 struct device_drv cpu_drv = {
 	.dname = "cpu",
 	.name = "CPU",
+	.supported_algos = POW_SHA256D | POW_SCRYPT,
 	.drv_detect = cpu_detect,
 	.thread_prepare = cpu_thread_prepare,
 	.can_limit_work = cpu_can_limit_work,

+ 7 - 8
driver-erupter.c

@@ -18,8 +18,8 @@
 #define ERUPTER_IO_SPEED 115200
 #define ERUPTER_HASH_TIME 0.0000000029761
 
-extern struct device_drv erupter_drv;
-extern struct device_drv erupter_drv_emerald;
+BFG_REGISTER_DRIVER(erupter_drv)
+BFG_REGISTER_DRIVER(erupter_drv_emerald)
 
 static bool _erupter_detect_one(const char *devpath, struct device_drv *drv)
 {
@@ -68,11 +68,8 @@ static int erupter_detect_auto(void)
 	return serial_autodetect(erupter_detect_one, "Block", "Erupter");
 }
 
-static void erupter_drv_init();
-
 static void erupter_detect()
 {
-	erupter_drv_init();
 	// Actual serial detection is handled by Icarus driver
 	serial_detect_auto_byname(&erupter_drv, erupter_detect_one, erupter_detect_auto);
 	serial_detect_auto_byname(&erupter_drv_emerald, erupter_emerald_detect_one, erupter_emerald_detect_auto);
@@ -93,14 +90,16 @@ static void erupter_drv_init()
 	erupter_drv.name = "BES";
 	erupter_drv.drv_detect = erupter_detect;
 	erupter_drv.identify_device = erupter_identify;
+	++erupter_drv.probe_priority;
 	
 	erupter_drv_emerald = erupter_drv;
 	erupter_drv_emerald.name = "BEE";
 }
 
 struct device_drv erupter_drv = {
-	// Needed to get to erupter_drv_init at all
-	.drv_detect = erupter_detect,
+	.drv_init = erupter_drv_init,
 };
 
-struct device_drv erupter_drv_emerald;
+struct device_drv erupter_drv_emerald = {
+	.drv_init = erupter_drv_init,
+};

+ 2 - 1
driver-icarus.c

@@ -169,7 +169,7 @@ static const char *MODE_UNKNOWN_STR = "unknown";
 //
 static int option_offset = -1;
 
-struct device_drv icarus_drv;
+BFG_REGISTER_DRIVER(icarus_drv)
 
 extern void convert_icarus_to_cairnsmore(struct cgpu_info *);
 
@@ -1309,6 +1309,7 @@ static void icarus_shutdown(struct thr_info *thr)
 struct device_drv icarus_drv = {
 	.dname = "icarus",
 	.name = "ICA",
+	.probe_priority = -120,
 	.drv_detect = icarus_detect,
 	.get_api_stats = icarus_drv_stats,
 	.thread_prepare = icarus_prepare,

+ 1 - 1
driver-knc.c

@@ -76,7 +76,7 @@ enum knc_i2c_core_status {
 	KNC_I2CSTATUS_ENABLED  = 3,
 };
 
-struct device_drv knc_drv;
+BFG_REGISTER_DRIVER(knc_drv)
 
 struct knc_device {
 	int i2c;

+ 1 - 1
driver-littlefury.c

@@ -35,7 +35,7 @@ enum littlefury_opcode {
 	LFOP_ADC     = 7,
 };
 
-struct device_drv littlefury_drv;
+BFG_REGISTER_DRIVER(littlefury_drv)
 
 static uint16_t crc16tab[] = {
 	0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,

+ 1 - 1
driver-metabank.c

@@ -32,7 +32,7 @@
 #include "spidevc.h"
 #include "tm_i2c.h"
 
-struct device_drv metabank_drv;
+BFG_REGISTER_DRIVER(metabank_drv)
 
 static
 bool metabank_spi_txrx(struct spi_port *port)

+ 1 - 1
driver-modminer.c

@@ -48,7 +48,7 @@
 
 #define FPGAID_ALL 4
 
-struct device_drv modminer_drv;
+BFG_REGISTER_DRIVER(modminer_drv)
 
 struct modminer_fpga_state {
 	bool work_running;

+ 2 - 2
driver-nanofury.c

@@ -30,7 +30,7 @@
 
 #define NANOFURY_MAX_BYTES_PER_SPI_TRANSFER 60			// due to MCP2210 limitation
 
-struct device_drv nanofury_drv;
+BFG_REGISTER_DRIVER(nanofury_drv)
 
 // Bit-banging reset, to reset more chips in chain - toggle for longer period... Each 3 reset cycles reset first chip in chain
 static
@@ -161,7 +161,7 @@ fail:
 }
 
 static
-bool nanofury_foundlowl(struct lowlevel_device_info * const info)
+bool nanofury_foundlowl(struct lowlevel_device_info * const info, __maybe_unused void *userp)
 {
 	const char * const product = info->product;
 	const char * const serial = info->serial;

+ 2 - 1
driver-opencl.c

@@ -787,7 +787,7 @@ char *set_intensity(char *arg)
 
 
 #ifdef HAVE_OPENCL
-struct device_drv opencl_api;
+BFG_REGISTER_DRIVER(opencl_api)
 
 char *print_ndevs_and_exit(int *ndevs)
 {
@@ -1769,6 +1769,7 @@ static void opencl_thread_shutdown(struct thr_info *thr)
 struct device_drv opencl_api = {
 	.dname = "opencl",
 	.name = "OCL",
+	.supported_algos = POW_SHA256D | POW_SCRYPT,
 	.drv_detect = opencl_detect,
 	.reinit_device = reinit_opencl_device,
 	.override_statline_temp2 = override_opencl_statline_temp,

+ 1 - 1
driver-proxy.c

@@ -18,7 +18,7 @@
 #include "miner.h"
 #include "util.h"
 
-struct device_drv proxy_drv;
+BFG_REGISTER_DRIVER(proxy_drv)
 
 static
 struct proxy_client *proxy_clients;

+ 5 - 4
driver-x6500.c

@@ -40,7 +40,7 @@
 #define X6500_DEFAULT_CLOCK  190
 #define X6500_MAXIMUM_CLOCK  250
 
-struct device_drv x6500_api;
+BFG_REGISTER_DRIVER(x6500_api)
 
 #define fromlebytes(ca, j)  (ca[j] | (((uint16_t)ca[j+1])<<8) | (((uint32_t)ca[j+2])<<16) | (((uint32_t)ca[j+3])<<24))
 
@@ -127,14 +127,15 @@ uint32_t x6500_get_register(struct jtag_port *jp, uint8_t addr)
 	return bits2int(buf, 32);
 }
 
-static bool x6500_foundlowl(struct lowlevel_device_info * const info)
+static bool x6500_foundlowl(struct lowlevel_device_info * const info, __maybe_unused void *userp)
 {
 	const char * const product = info->product;
 	const char * const serial = info->serial;
 	if (info->lowl != &lowl_ft232r)
 	{
-		applog(LOG_WARNING, "%s: Matched \"%s\" serial \"%s\", but lowlevel driver is not ft232r!",
-		       __func__, product, serial);
+		if (info->lowl != &lowl_usb)
+			applog(LOG_WARNING, "%s: Matched \"%s\" serial \"%s\", but lowlevel driver is not ft232r!",
+			       __func__, product, serial);
 		return false;
 	}
 	

+ 53 - 29
driver-ztex.c

@@ -27,11 +27,12 @@
 #include "dynclock.h"
 #include "fpgautils.h"
 #include "libztex.h"
+#include "lowlevel.h"
 #include "util.h"
 
 #define GOLDEN_BACKLOG 5
 
-struct device_drv ztex_drv;
+BFG_REGISTER_DRIVER(ztex_drv)
 
 // Forward declarations
 static void ztex_disable(struct thr_info* thr);
@@ -75,45 +76,68 @@ static struct cgpu_info *ztex_setup(struct libztex_device *dev, int fpgacount)
 	return ztex;
 }
 
-static int ztex_autodetect(void)
+static
+bool ztex_foundlowl(struct lowlevel_device_info * const info, __maybe_unused void *userp)
 {
-	int cnt;
-	int i;
+	const char * const product = info->product;
+	const char * const serial = info->serial;
+	if (info->lowl != &lowl_usb)
+	{
+		applog(LOG_WARNING, "%s: Matched \"%s\" serial \"%s\", but lowlevel driver is not usb!",
+		       __func__, product, serial);
+		return false;
+	}
+	
+	libusb_device * const usbdev = info->lowl_data;
+	
+	const enum ztex_check_result err = libztex_checkDevice(usbdev);
+	switch (err)
+	{
+		case CHECK_ERROR:
+			applogr(false, LOG_ERR, "%s: Can not check device %s", ztex_drv.dname, info->devid);
+		case CHECK_IS_NOT_ZTEX:
+			return false;
+		case CHECK_OK:
+			break;
+		case CHECK_RESCAN:
+			bfg_need_detect_rescan = true;
+			return false;
+	}
+	
 	int fpgacount;
-	int totaldevs = 0;
-	struct libztex_dev_list **ztex_devices;
 	struct libztex_device *ztex_master;
 	struct cgpu_info *ztex;
+	
+	ztex_master = libztex_prepare_device2(usbdev);
+	if (!ztex_master)
+		applogr(false, LOG_ERR, "%s: libztex_prepare_device2 failed on %s", ztex_drv.dname, info->devid);
+	
+	if (bfg_claim_usb(&ztex_drv, true, ztex_master->usbbus, ztex_master->usbaddress))
+		return false;
+	ztex_master->root = ztex_master;
+	fpgacount = libztex_numberOfFpgas(ztex_master);
+	ztex_master->handles = fpgacount;
+	ztex = ztex_setup(ztex_master, fpgacount);
 
-	cnt = libztex_scanDevices(&ztex_devices);
-	if (cnt > 0)
-		applog(LOG_INFO, "Found %d ztex board%s", cnt, cnt > 1 ? "s" : "");
-
-	for (i = 0; i < cnt; i++) {
-		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;
-		fpgacount = libztex_numberOfFpgas(ztex_master);
-		ztex_master->handles = fpgacount;
-		ztex = ztex_setup(ztex_master, fpgacount);
-
-		totaldevs += fpgacount;
-
-		if (fpgacount > 1)
-			pthread_mutex_init(&ztex->device_ztex->mutex, NULL);
-	}
+	if (fpgacount > 1)
+		pthread_mutex_init(&ztex->device_ztex->mutex, NULL);
+	
+	return true;
+}
 
-	if (cnt > 0)
-		libztex_freeDevList(ztex_devices);
+static bool ztex_detect_one(const char *serial)
+{
+	return lowlevel_detect_serial(ztex_foundlowl, serial);
+}
 
-	return totaldevs;
+static int ztex_autodetect()
+{
+	return lowlevel_detect(ztex_foundlowl, "btcminer for ZTEX");
 }
 
 static void ztex_detect()
 {
-	// This wrapper ensures users can specify -S ztex:noauto to disable it
-	noserial_detect(&ztex_drv, ztex_autodetect);
+	generic_detect(&ztex_drv, ztex_detect_one, ztex_autodetect, 0);
 }
 
 static bool ztex_change_clock_func(struct thr_info *thr, int bestM)

+ 179 - 178
fpgautils.c

@@ -83,63 +83,43 @@ enum {
 #endif
 
 #include "logging.h"
+#include "lowlevel.h"
 #include "miner.h"
 #include "util.h"
 
 #include "fpgautils.h"
 
-#define SEARCH_NEEDLES_BEGIN()  {  \
-	const char *needle;  \
-	bool __cont = false;  \
-	va_list ap;  \
-	va_copy(ap, needles);  \
-	while ( (needle = va_arg(ap, const char *)) )  \
-	{
+struct lowlevel_driver lowl_vcom;
 
-#define SEARCH_NEEDLES_END(...)  \
-	}  \
-	va_end(ap);  \
-	if (__cont)  \
-	{  \
-		__VA_ARGS__;  \
-	}  \
-}
+struct detectone_meta_info_t detectone_meta_info;
 
-static inline
-bool search_needles(const char *haystack, va_list needles)
+void clear_detectone_meta_info(void)
 {
-	bool rv = true;
-	SEARCH_NEEDLES_BEGIN()
-		if (!strstr(haystack, needle))
-		{
-			rv = false;
-			break;
-		}
-	SEARCH_NEEDLES_END()
-	return rv;
+	detectone_meta_info = (struct detectone_meta_info_t){
+		.manufacturer = NULL,
+	};
 }
 
-#define SEARCH_NEEDLES(haystack)  search_needles(haystack, needles)
+static char *_vcom_unique_id(const char *);
 
-static
-int _detectone_wrap(const detectone_func_t detectone, const char * const param, const char *fname)
+struct lowlevel_device_info *_vcom_devinfo_findorcreate(struct lowlevel_device_info ** const devinfo_list, const char * const devpath)
 {
-	if (bfg_claim_serial(NULL, true, param))
+	struct lowlevel_device_info *devinfo;
+	char * const devid = _vcom_unique_id(devpath);
+	HASH_FIND_STR(*devinfo_list, devid, devinfo);
+	if (!devinfo)
 	{
-		applog(LOG_DEBUG, "%s: %s is already claimed, skipping probe", fname, param);
-		return 0;
+		devinfo = malloc(sizeof(*devinfo));
+		*devinfo = (struct lowlevel_device_info){
+			.lowl = &lowl_vcom,
+			.path = strdup(devpath),
+			.devid = devid,
+		};
+		HASH_ADD_KEYPTR(hh, *devinfo_list, devinfo->devid, strlen(devid), devinfo);
 	}
-	return detectone(param);
-}
-#define detectone(param)  _detectone_wrap(detectone, param, __func__)
-
-struct detectone_meta_info_t detectone_meta_info;
-
-void clear_detectone_meta_info(void)
-{
-	detectone_meta_info = (struct detectone_meta_info_t){
-		.manufacturer = NULL,
-	};
+	else
+		free(devid);
+	return devinfo;
 }
 
 #ifdef HAVE_LIBUDEV
@@ -177,14 +157,15 @@ char *_decode_udev_enc_dup(const char *s)
 }
 
 static
-int _serial_autodetect_udev(detectone_func_t detectone, va_list needles)
+void _vcom_devinfo_scan_udev(struct lowlevel_device_info ** const devinfo_list)
 {
 	struct udev *udev = udev_new();
 	struct udev_enumerate *enumerate = udev_enumerate_new(udev);
 	struct udev_list_entry *list_entry;
-	char found = 0;
+	struct lowlevel_device_info *devinfo;
 
 	udev_enumerate_add_match_subsystem(enumerate, "tty");
+	udev_enumerate_add_match_property(enumerate, "ID_SERIAL", "*");
 	udev_enumerate_scan_devices(enumerate);
 	udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) {
 		struct udev_device *device = udev_device_new_from_syspath(
@@ -194,71 +175,46 @@ int _serial_autodetect_udev(detectone_func_t detectone, va_list needles)
 		if (!device)
 			continue;
 
-		const char *model = udev_device_get_property_value(device, "ID_MODEL");
-		if (!(model && SEARCH_NEEDLES(model)))
-		{
-			udev_device_unref(device);
-			continue;
-		}
-
-		detectone_meta_info = (struct detectone_meta_info_t){
-			.manufacturer = _decode_udev_enc_dup(udev_device_get_property_value(device, "ID_VENDOR_ENC")),
-			.product = _decode_udev_enc_dup(udev_device_get_property_value(device, "ID_MODEL_ENC")),
-			.serial = _decode_udev_enc_dup(udev_device_get_property_value(device, "ID_SERIAL_SHORT")),
-		};
+		const char * const devpath = udev_device_get_devnode(device);
+		devinfo = _vcom_devinfo_findorcreate(devinfo_list, devpath);
+		
+		BFGINIT(devinfo->manufacturer, _decode_udev_enc_dup(udev_device_get_property_value(device, "ID_VENDOR_ENC")));
+		BFGINIT(devinfo->product, _decode_udev_enc_dup(udev_device_get_property_value(device, "ID_MODEL_ENC")));
+		BFGINIT(devinfo->serial, _decode_udev_enc_dup(udev_device_get_property_value(device, "ID_SERIAL_SHORT")));
 		
-		const char *devpath = udev_device_get_devnode(device);
-		if (devpath && detectone(devpath))
-			++found;
-
-		free((void*)detectone_meta_info.manufacturer);
-		free((void*)detectone_meta_info.product);
-		free((void*)detectone_meta_info.serial);
 		udev_device_unref(device);
 	}
 	udev_enumerate_unref(enumerate);
 	udev_unref(udev);
-	clear_detectone_meta_info();
-
-	return found;
 }
-#else
-#	define _serial_autodetect_udev(...)  (0)
 #endif
 
 #ifndef WIN32
 static
-int _serial_autodetect_devserial(detectone_func_t detectone, va_list needles)
+void _vcom_devinfo_scan_devserial(struct lowlevel_device_info ** const devinfo_list)
 {
 	DIR *D;
 	struct dirent *de;
 	const char udevdir[] = "/dev/serial/by-id";
 	char devpath[sizeof(udevdir) + 1 + NAME_MAX];
 	char *devfile = devpath + sizeof(udevdir);
-	char found = 0;
-
-	// No way to split this out of the filename reliably
-	clear_detectone_meta_info();
+	struct lowlevel_device_info *devinfo;
 	
 	D = opendir(udevdir);
 	if (!D)
-		return 0;
+		return;
 	memcpy(devpath, udevdir, sizeof(udevdir) - 1);
 	devpath[sizeof(udevdir) - 1] = '/';
 	while ( (de = readdir(D)) ) {
-		if (!SEARCH_NEEDLES(de->d_name))
+		if (strncmp(de->d_name, "usb-", 4))
 			continue;
-		
 		strcpy(devfile, de->d_name);
-		if (detectone(devpath))
-			++found;
+		devinfo = _vcom_devinfo_findorcreate(devinfo_list, devpath);
+		if (!(devinfo->manufacturer || devinfo->product || devinfo->serial))
+			devinfo->product = strdup(devfile);
 	}
 	closedir(D);
-
-	return found;
 }
-#else
-#	define _serial_autodetect_devserial(...)  (0)
 #endif
 
 #ifndef WIN32
@@ -288,8 +244,9 @@ char *_sysfs_do_read(char *buf, size_t bufsz, const char *devpath, char *devfile
 }
 
 static
-void _sysfs_find_tty(detectone_func_t detectone, char *devpath, char *devfile, const char *prod, char *pfound)
+void _sysfs_find_tty(char *devpath, char *devfile, const char *prod, struct lowlevel_device_info ** const devinfo_list)
 {
+	struct lowlevel_device_info *devinfo;
 	DIR *DT;
 	struct dirent *de;
 	char ttybuf[0x10] = "/dev/";
@@ -308,22 +265,17 @@ void _sysfs_find_tty(detectone_func_t detectone, char *devpath, char *devfile, c
 		{
 			// "tty" directory: recurse (needed for ttyACM)
 			sprintf(devfile, "%s/tty", mydevfile);
-			_sysfs_find_tty(detectone, devpath, devfile, prod, pfound);
+			_sysfs_find_tty(devpath, devfile, prod, devinfo_list);
 			continue;
 		}
 		if (strncmp(&de->d_name[3], "USB", 3) && strncmp(&de->d_name[3], "ACM", 3))
 			continue;
 		
-		
-		detectone_meta_info = (struct detectone_meta_info_t){
-			.manufacturer = _sysfs_do_read(manuf, sizeof(manuf), devpath, devfile, "/manufacturer"),
-			.product = prod,
-			.serial = _sysfs_do_read(serial, sizeof(serial), devpath, devfile, "/serial"),
-		};
-		
 		strcpy(&ttybuf[5], de->d_name);
-		if (detectone(ttybuf))
-			++*pfound;
+		devinfo = _vcom_devinfo_findorcreate(devinfo_list, ttybuf);
+		BFGINIT(devinfo->manufacturer, maybe_strdup(_sysfs_do_read(manuf, sizeof(manuf), devpath, devfile, "/manufacturer")));
+		BFGINIT(devinfo->product, maybe_strdup(prod));
+		BFGINIT(devinfo->serial, maybe_strdup(_sysfs_do_read(serial, sizeof(serial), devpath, devfile, "/serial")));
 	}
 	closedir(DT);
 	
@@ -332,7 +284,7 @@ out:
 }
 
 static
-int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
+void _vcom_devinfo_scan_sysfs(struct lowlevel_device_info ** const devinfo_list)
 {
 	DIR *D, *DS;
 	struct dirent *de;
@@ -341,12 +293,11 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 	char devpath[sizeof(devroot) + (NAME_MAX * 3)];
 	char prod[0x40];
 	char *devfile, *upfile;
-	char found = 0;
 	size_t len, len2;
 	
 	D = opendir(devroot);
 	if (!D)
-		return 0;
+		return;
 	memcpy(devpath, devroot, devrootlen);
 	devpath[devrootlen] = '/';
 	while ( (de = readdir(D)) )
@@ -357,10 +308,7 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 		devfile = upfile + len;
 		
 		if (!_sysfs_do_read(prod, sizeof(prod), devpath, devfile, "/product"))
-			continue;
-		
-		if (!SEARCH_NEEDLES(prod))
-			continue;
+			prod[0] = '\0';
 		
 		devfile[0] = '\0';
 		DS = opendir(devpath);
@@ -377,17 +325,12 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 			len2 = strlen(de->d_name);
 			memcpy(devfile, de->d_name, len2 + 1);
 			
-			_sysfs_find_tty(detectone, devpath, devfile, prod, &found);
+			_sysfs_find_tty(devpath, devfile, prod[0] ? prod : NULL, devinfo_list);
 		}
 		closedir(DS);
 	}
 	closedir(D);
-	clear_detectone_meta_info();
-	
-	return found;
 }
-#else
-#	define _serial_autodetect_sysfs(...)  (0)
 #endif
 
 #ifdef HAVE_WIN_DDKUSB
@@ -455,11 +398,12 @@ char *windows_usb_get_string(HANDLE hubh, const int portno, const uint8_t descid
 	return ucs2tochar_dup(desc->bString, desc->bLength);
 }
 
-static void _serial_autodetect_windows__hub(detectone_func_t, va_list, int *, const char *);
+static void _vcom_devinfo_scan_windows__hub(detectone_func_t, va_list, int *, const char *);
 
 static
-void _serial_autodetect_windows__hubport(detectone_func_t detectone, va_list needles, int * const foundp, HANDLE hubh, const int portno)
+void _vcom_devinfo_scan_windows__hubport(struct lowlevel_device_info ** const devinfo_list, HANDLE hubh, const int portno)
 {
+	struct lowlevel_device_info *devinfo;
 	const size_t conninfosz = sizeof(USB_NODE_CONNECTION_INFORMATION) + (sizeof(USB_PIPE_INFO) * 30);
 	uint8_t buf[conninfosz];
 	USB_NODE_CONNECTION_INFORMATION * const conninfo = (USB_NODE_CONNECTION_INFORMATION *)buf;
@@ -477,26 +421,18 @@ void _serial_autodetect_windows__hubport(detectone_func_t detectone, va_list nee
 	{
 		const char * const hubpath = windows_usb_get_port_path(hubh, portno);
 		if (hubpath)
-			_serial_autodetect_windows__hub(detectone, needles, foundp, hubpath);
+			_vcom_devinfo_scan_windows__hub(devinfo_list, hubpath);
 		return;
 	}
 	
 	const USB_DEVICE_DESCRIPTOR * const devdesc = &conninfo->DeviceDescriptor;
-	char * const product = windows_usb_get_string(hubh, portno, devdesc->iProduct);
-	if (!product)
-		return;
-	char *serial = NULL;
-	if (!search_needles(product, needles))
+	char * const serial = windows_usb_get_string(hubh, portno, devdesc->iSerialNumber);
+	if (!serial)
 	{
 out:
-		free(product);
 		free(serial);
 		return;
 	}
-	
-	serial = windows_usb_get_string(hubh, portno, devdesc->iSerialNumber);
-	if (!serial)
-		goto out;
 	const size_t slen = strlen(serial);
 	char subkey[52 + slen + 18 + 1];
 	sprintf(subkey, "SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_%04x&PID_%04x\\%s\\Device Parameters",
@@ -523,24 +459,17 @@ out:
 		goto out;
 	}
 	
-	char * const manuf = windows_usb_get_string(hubh, portno, devdesc->iManufacturer);
-	
-	detectone_meta_info = (struct detectone_meta_info_t){
-		.manufacturer = manuf,
-		.product = product,
-		.serial = serial,
-	};
-	
-	if (detectone(devpath))
-		++*foundp;
-	
-	clear_detectone_meta_info();
-	free(manuf);
-	goto out;
+	devinfo = _vcom_devinfo_findorcreate(devinfo_list, devpath);
+	BFGINIT(devinfo->manufacturer, windows_usb_get_string(hubh, portno, devdesc->iManufacturer))
+	BFGINIT(devinfo->product, windows_usb_get_string(hubh, portno, devdesc->iProduct));
+	if (devinfo->serial)
+		free(serial);
+	else
+		devinfo->serial = serial;
 }
 
 static
-void _serial_autodetect_windows__hub(detectone_func_t detectone, va_list needles, int * const foundp, const char * const hubpath)
+void _vcom_devinfo_scan_windows__hub(struct lowlevel_device_info ** const devinfo_list, const char * const hubpath)
 {
 	HANDLE hubh;
 	USB_NODE_INFORMATION nodeinfo;
@@ -559,7 +488,7 @@ void _serial_autodetect_windows__hub(detectone_func_t detectone, va_list needles
 	
 	const int portcount = nodeinfo.u.HubInformation.HubDescriptor.bNumberOfPorts;
 	for (int i = 1; i <= portcount; ++i)
-		_serial_autodetect_windows__hubport(detectone, needles, foundp, hubh, i);
+		_vcom_devinfo_scan_windows__hubport(devinfo_list, hubh, i);
 	
 	CloseHandle(hubh);
 }
@@ -588,7 +517,7 @@ char *windows_usb_get_root_hub_path(HANDLE hcntlrh)
 }
 
 static
-void _serial_autodetect_windows__hcntlr(detectone_func_t detectone, va_list needles, int * const foundp, HDEVINFO *devinfo, const int i)
+void _vcom_devinfo_scan_windows__hcntlr(struct lowlevel_device_info ** const devinfo_list, HDEVINFO *devinfo, const int i)
 {
 	SP_DEVICE_INTERFACE_DATA devifacedata = {
 		.cbSize = sizeof(devifacedata),
@@ -607,14 +536,13 @@ void _serial_autodetect_windows__hcntlr(detectone_func_t detectone, va_list need
 		applogfailinfor(, LOG_DEBUG, "open USB host controller device", "%s", bfg_strerror(GetLastError(), BST_SYSTEM));
 	char * const hubpath = windows_usb_get_root_hub_path(hcntlrh);
 	CloseHandle(hcntlrh);
-	_serial_autodetect_windows__hub(detectone, needles, foundp, hubpath);
+	_vcom_devinfo_scan_windows__hub(devinfo_list, hubpath);
 	free(hubpath);
 }
 
 static
-int _serial_autodetect_windows(detectone_func_t detectone, va_list needles)
+void _vcom_devinfo_scan_windows(struct lowlevel_device_info ** const devinfo_list)
 {
-	int found = 0;
 	HDEVINFO devinfo;
 	devinfo = SetupDiGetClassDevs(&WIN_GUID_DEVINTERFACE_USB_HOST_CONTROLLER, NULL, NULL, (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
 	SP_DEVINFO_DATA devinfodata = {
@@ -622,10 +550,8 @@ int _serial_autodetect_windows(detectone_func_t detectone, va_list needles)
 	};
 	
 	for (int i = 0; SetupDiEnumDeviceInfo(devinfo, i, &devinfodata); ++i)
-		_serial_autodetect_windows__hcntlr(detectone, needles, &found, &devinfo, i);
+		_vcom_devinfo_scan_windows__hcntlr(devinfo_list, &devinfo, i);
 	SetupDiDestroyDeviceInfoList(devinfo);
-	
-	return found;
 }
 
 #endif
@@ -648,14 +574,14 @@ char *_ftdi_get_string(char *buf, int i, DWORD flags)
 }
 
 static
-int _serial_autodetect_ftdi(detectone_func_t detectone, va_list needles)
+void _vcom_devinfo_scan_ftdi(struct lowlevel_device_info ** const devinfo_list)
 {
 	char devpath[] = "\\\\.\\COMnnnnn";
 	char *devpathnum = &devpath[7];
 	char **bufptrs;
 	char *buf;
 	char serial[64];
-	int found = 0;
+	struct lowlevel_device_info *devinfo;
 	DWORD i;
 
 	FT_STATUS ftStatus;
@@ -689,14 +615,10 @@ int _serial_autodetect_ftdi(detectone_func_t detectone, va_list needles)
 		goto out;
 	}
 	
-	clear_detectone_meta_info();
 	for (i = numDevs; i > 0; ) {
 		--i;
 		bufptrs[i][64] = '\0';
 		
-		if (!SEARCH_NEEDLES(bufptrs[i]))
-			continue;
-		
 		FT_HANDLE ftHandle;
 		if (FT_OK != FT_Open(i, &ftHandle))
 			continue;
@@ -707,45 +629,101 @@ int _serial_autodetect_ftdi(detectone_func_t detectone, va_list needles)
 			continue;
 		
 		applog(LOG_ERR, "FT_GetComPortNumber(%p (%ld), %ld)", ftHandle, (long)i, (long)lComPortNumber);
-		detectone_meta_info = (struct detectone_meta_info_t){
-			.product = bufptrs[i],
-			.serial = _ftdi_get_string(serial, i, FT_OPEN_BY_SERIAL_NUMBER),
-		};
-		
 		sprintf(devpathnum, "%d", (int)lComPortNumber);
 		
-		if (detectone(devpath))
-			++found;
+		devinfo = _vcom_devinfo_findorcreate(devinfo_list, devpath);
+		BFGINIT(devinfo->product, (bufptrs[i] && bufptrs[i][0]) ? strdup(bufptrs[i]) : NULL);
+		BFGINIT(devinfo->serial, maybe_strdup(_ftdi_get_string(serial, i, FT_OPEN_BY_SERIAL_NUMBER)));
 	}
 
 out:
-	clear_detectone_meta_info();
 	dlclose(dll);
-	return found;
 }
+#endif
+
+#ifdef WIN32
+extern void _vcom_devinfo_scan_querydosdevice(struct lowlevel_device_info **);
 #else
-#	define _serial_autodetect_ftdi(...)  (0)
+extern void _vcom_devinfo_scan_lsdev(struct lowlevel_device_info **);
 #endif
 
-#undef detectone
+bool _serial_autodetect_found_cb(struct lowlevel_device_info * const devinfo, void *userp)
+{
+	detectone_func_t detectone = userp;
+	if (bfg_claim_any(NULL, devinfo->path, devinfo->devid))
+	{
+		applog(LOG_DEBUG, "%s (%s) is already claimed, skipping probe", devinfo->path, devinfo->devid);
+		return false;
+	}
+	if (devinfo->lowl != &lowl_vcom)
+	{
+		if (devinfo->lowl != &lowl_usb)
+			applog(LOG_WARNING, "Non-VCOM %s (%s) matched", devinfo->path, devinfo->devid);
+		return false;
+	}
+	detectone_meta_info = (struct detectone_meta_info_t){
+		.manufacturer = devinfo->manufacturer,
+		.product = devinfo->product,
+		.serial = devinfo->serial,
+	};
+	const bool rv = detectone(devinfo->path);
+	clear_detectone_meta_info();
+	return rv;
+}
 
 int _serial_autodetect(detectone_func_t detectone, ...)
 {
-	int rv;
 	va_list needles;
+	char *needles_array[0x10];
+	int needlecount = 0;
 	
 	va_start(needles, detectone);
-	rv = (
-		_serial_autodetect_udev     (detectone, needles) ?:
-		_serial_autodetect_sysfs    (detectone, needles) ?:
-		_serial_autodetect_devserial(detectone, needles) ?:
+	while ( (needles_array[needlecount++] = va_arg(needles, void *)) )
+	{}
+	va_end(needles);
+	
+	return _lowlevel_detect(_serial_autodetect_found_cb, NULL, (const char **)needles_array, detectone);
+}
+
+static
+struct lowlevel_device_info *vcom_devinfo_scan()
+{
+	struct lowlevel_device_info *devinfo_hash = NULL;
+	struct lowlevel_device_info *devinfo_list = NULL;
+	struct lowlevel_device_info *devinfo, *tmp;
+	
+	// All 3 USB Strings available:
+#ifndef WIN32
+	_vcom_devinfo_scan_sysfs(&devinfo_hash);
+#endif
 #ifdef HAVE_WIN_DDKUSB
-		_serial_autodetect_windows  (detectone, needles) ?:
+	_vcom_devinfo_scan_windows(&devinfo_hash);
 #endif
-		_serial_autodetect_ftdi     (detectone, needles) ?:
-		0);
-	va_end(needles);
-	return rv;
+#ifdef HAVE_LIBUDEV
+	_vcom_devinfo_scan_udev(&devinfo_hash);
+#endif
+	// Missing Manufacturer:
+#ifdef WIN32
+	_vcom_devinfo_scan_ftdi(&devinfo_hash);
+#endif
+	// All blobbed together:
+#ifndef WIN32
+	_vcom_devinfo_scan_devserial(&devinfo_hash);
+#endif
+	// No info:
+#ifdef WIN32
+	_vcom_devinfo_scan_querydosdevice(&devinfo_hash);
+#else
+	_vcom_devinfo_scan_lsdev(&devinfo_hash);
+#endif
+	
+	// Convert hash to simple list
+	HASH_ITER(hh, devinfo_hash, devinfo, tmp)
+	{
+		LL_PREPEND(devinfo_list, devinfo);
+	}
+	
+	return devinfo_list;
 }
 
 
@@ -803,10 +781,11 @@ struct device_drv *bfg_claim_any2(struct device_drv * const api, const char * co
 	return bfg_claim_any(api, verbose, devpath);
 }
 
-struct device_drv *bfg_claim_serial(struct device_drv * const api, const bool verbose, const char * const devpath)
+static
+char *_vcom_unique_id(const char * const devpath)
 {
 #ifndef WIN32
-	char devs[6 + (sizeof(dev_t) * 2) + 1];
+	char *devs = malloc(6 + (sizeof(dev_t) * 2) + 1);
 	{
 		struct stat my_stat;
 		if (stat(devpath, &my_stat))
@@ -823,18 +802,35 @@ struct device_drv *bfg_claim_serial(struct device_drv * const api, const bool ve
 			return NULL;
 	char dummy;
 	const int sz = snprintf(&dummy, 1, "%d", com);
-	char devs[4 + sz + 1];
+	char *devs = malloc(4 + sz + 1);
 	sprintf(devs, "com:%d", com);
 #endif
-	
-	return bfg_claim_any(api, (verbose ? devpath : NULL), devs);
+	return devs;
 }
 
-struct device_drv *bfg_claim_usb(struct device_drv * const api, const bool verbose, const uint8_t usbbus, const uint8_t usbaddr)
+struct device_drv *bfg_claim_serial(struct device_drv * const api, const bool verbose, const char * const devpath)
+{
+	char * const devs = _vcom_unique_id(devpath);
+	if (!devs)
+		return false;
+	struct device_drv * const rv = bfg_claim_any(api, (verbose ? devpath : NULL), devs);
+	free(devs);
+	return rv;
+}
+
+char *bfg_make_devid_usb(const uint8_t usbbus, const uint8_t usbaddr)
 {
-	char devpath[12];
+	char * const devpath = malloc(12);
 	sprintf(devpath, "usb:%03u:%03u", (unsigned)usbbus, (unsigned)usbaddr);
-	return bfg_claim_any(api, verbose ? "" : NULL, devpath);
+	return devpath;
+}
+
+struct device_drv *bfg_claim_usb(struct device_drv * const api, const bool verbose, const uint8_t usbbus, const uint8_t usbaddr)
+{
+	char * const devpath = bfg_make_devid_usb(usbbus, usbaddr);
+	struct device_drv * const rv = bfg_claim_any(api, verbose ? "" : NULL, devpath);
+	free(devpath);
+	return rv;
 }
 
 #ifdef HAVE_LIBUSB
@@ -1396,3 +1392,8 @@ int get_serial_cts(const int fd)
 	return (flags & MS_CTS_ON) ? 1 : 0;
 }
 #endif // ! WIN32
+
+struct lowlevel_driver lowl_vcom = {
+	.dname = "vcom",
+	.devinfo_scan = vcom_devinfo_scan,
+};

+ 2 - 0
fpgautils.h

@@ -33,7 +33,9 @@ extern struct device_drv *bfg_claim_any2(struct device_drv *, const char *verbos
 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 char *bfg_make_devid_usb(uint8_t usbbus, uint8_t usbaddr);
 extern struct device_drv *bfg_claim_usb(struct device_drv * const, const bool verbose, const uint8_t usbbus, const uint8_t usbaddr);
+#define bfg_make_devid_libusb(dev)  bfg_make_devid_usb(libusb_get_bus_number(dev), libusb_get_device_address(dev))
 #define bfg_claim_libusb(api, verbose, dev)  bfg_claim_usb(api, verbose, libusb_get_bus_number(dev), libusb_get_device_address(dev))
 #define bfg_claim_hid(api, verbose, path)  bfg_claim_any2(api, (verbose)?"":NULL, "hid", path)
 

+ 19 - 72
ft232r.c

@@ -39,82 +39,28 @@ void ft232r_devinfo_free(struct lowlevel_device_info * const info)
 }
 
 static
-struct lowlevel_device_info *ft232r_devinfo_scan()
+bool _ft232r_devinfo_scan_cb(struct lowlevel_device_info * const usbinfo, void * const userp)
 {
-	struct lowlevel_device_info *devinfo_list = NULL;
-	ssize_t count, n, i;
-	libusb_device **list;
-	struct libusb_device_descriptor desc;
-	libusb_device_handle *handle;
-	struct lowlevel_device_info *info;
-	int err;
-	unsigned char buf[0x100];
-	int skipped = 0;
-
-	if (unlikely(!have_libusb))
-		return NULL;
+	struct lowlevel_device_info **devinfo_list_p = userp, *info;
 	
-	count = libusb_get_device_list(NULL, &list);
-	if (unlikely(count < 0)) {
-		applog(LOG_ERR, "ft232r_scan: Error getting USB device list: %s", bfg_strerror(count, BST_LIBUSB));
-		return NULL;
-	}
-
-	for (i = 0; i < count; ++i) {
-		if (bfg_claim_libusb(NULL, false, list[i]))
-		{
-			++skipped;
-			continue;
-		}
-		
-		err = libusb_get_device_descriptor(list[i], &desc);
-		if (unlikely(err)) {
-			applog(LOG_ERR, "ft232r_scan: Error getting device descriptor: %s", bfg_strerror(err, BST_LIBUSB));
-			continue;
-		}
-		if (!(desc.idVendor == FT232R_IDVENDOR && desc.idProduct == FT232R_IDPRODUCT)) {
-			applog(LOG_DEBUG, "ft232r_scan: Found %04x:%04x - not a ft232r", desc.idVendor, desc.idProduct);
-			continue;
-		}
-
-		err = libusb_open(list[i], &handle);
-		if (unlikely(err)) {
-			applog(LOG_ERR, "ft232r_scan: Error opening device: %s", bfg_strerror(err, BST_LIBUSB));
-			continue;
-		}
-
-		n = libusb_get_string_descriptor_ascii(handle, desc.iProduct, buf, sizeof(buf)-1);
-		if (unlikely(n < 0)) {
-			libusb_close(handle);
-			applog(LOG_ERR, "ft232r_scan: Error getting iProduct string: %s", bfg_strerror(n, BST_LIBUSB));
-			continue;
-		}
-		buf[n] = '\0';
-		info = malloc(sizeof(struct lowlevel_device_info));
-		*info = (struct lowlevel_device_info){
-			.lowl = &lowl_ft232r,
-		};
-		info->product = strdup((char*)buf);
-
-		n = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf)-1);
-		libusb_close(handle);
-		if (unlikely(n < 0)) {
-			applog(LOG_ERR, "ft232r_scan: Error getting iSerialNumber string: %s", bfg_strerror(n, BST_LIBUSB));
-			n = 0;
-		}
-		buf[n] = '\0';
-		info->serial = strdup((char*)buf);
-		info->lowl_data = libusb_ref_device(list[i]);
-		
-		LL_PREPEND(devinfo_list, info);
-
-		applog(LOG_DEBUG, "ft232r_scan: Found \"%s\" serial \"%s\"", info->product, info->serial);
-	}
+	info = malloc(sizeof(*info));
+	*info = (struct lowlevel_device_info){
+		.lowl = &lowl_ft232r,
+		.lowl_data = libusb_ref_device(usbinfo->lowl_data),
+	};
+	lowlevel_devinfo_semicpy(info, usbinfo);
+	LL_PREPEND(*devinfo_list_p, info);
+	
+	// Never *consume* the lowl_usb entry - especially since this is during the scan!
+	return false;
+}
 
-	libusb_free_device_list(list, 1);
+static
+struct lowlevel_device_info *ft232r_devinfo_scan()
+{
+	struct lowlevel_device_info *devinfo_list = NULL;
 	
-	if (skipped)
-		applog(LOG_DEBUG, "%s: Skipping probe of %d claimed devices", __func__, skipped);
+	lowlevel_detect_id(_ft232r_devinfo_scan_cb, &devinfo_list, &lowl_usb, FT232R_IDVENDOR, FT232R_IDPRODUCT);
 	
 	return devinfo_list;
 }
@@ -379,6 +325,7 @@ bool ft232r_get_cbus_bits(struct ft232r_device_handle *dev, bool *out_sio0, bool
 }
 
 struct lowlevel_driver lowl_ft232r = {
+	.dname = "ft232r",
 	.devinfo_scan = ft232r_devinfo_scan,
 	.devinfo_free = ft232r_devinfo_free,
 };

+ 26 - 144
libztex.c

@@ -98,14 +98,6 @@ static int libztex_get_string_descriptor_ascii(libusb_device_handle *dev, uint8_
 	return LIBUSB_SUCCESS;
 }
 
-enum check_result
-{
-	CHECK_ERROR,
-	CHECK_IS_NOT_ZTEX,
-	CHECK_OK,
-	CHECK_RESCAN,
-};
-
 static bool libztex_firmwareReset(struct libusb_device_handle *hndl, bool enable)
 {
 	uint8_t reset = enable ? 1 : 0;
@@ -119,7 +111,7 @@ static bool libztex_firmwareReset(struct libusb_device_handle *hndl, bool enable
 	return 0;
 }
 
-static enum check_result libztex_checkDevice(struct libusb_device *dev)
+enum ztex_check_result libztex_checkDevice(struct libusb_device *dev)
 {
 	libusb_device_handle *hndl = NULL;
 	struct libusb_device_descriptor desc;
@@ -589,53 +581,56 @@ int libztex_suspend(struct libztex_device *ztex)
 	}
 }
 
-int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** ztex)
+struct libztex_device *libztex_prepare_device2(struct libusb_device * const dev)
 {
-	struct libztex_device *newdev = *ztex;
+	struct libztex_device *newdev;
 	int i, cnt, err;
 	unsigned char buf[64];
 
+	newdev = malloc(sizeof(*newdev));
+	if (!newdev)
+		applogr(NULL, LOG_ERR, "%s: Failed to malloc libztex_device", __func__);
+	
+	newdev->bitFileName = NULL;
+	newdev->numberOfFpgas = -1;
+	
 	dclk_prepare(&newdev->dclk);
 	newdev->dclk.freqMinM = 0;
 	err = libusb_open(dev, &newdev->hndl);
 	if (err != LIBUSB_SUCCESS) {
 		applog(LOG_ERR, "%s: Can not open ZTEX device: %s", __func__, bfg_strerror(err, BST_LIBUSB));
-		return CHECK_ERROR;
+		return NULL;
 	}
 
 	err = libusb_get_device_descriptor(dev, &newdev->descriptor);
 	if (unlikely(err != 0)) {
-		applog(LOG_ERR, "Ztex check device: Failed to open read descriptor with error %d", err);
-		return CHECK_ERROR;
+		applogr(NULL, LOG_ERR, "%s: Failed to open read descriptor: %s", __func__, bfg_strerror(err, BST_LIBUSB));
 	}
 
 	cnt = libztex_get_string_descriptor_ascii(newdev->hndl, newdev->descriptor.iSerialNumber, newdev->snString, sizeof(newdev->snString));
 	if (unlikely(cnt < 0)) {
-		applog(LOG_ERR, "Ztex check device: Failed to read device snString with err %d", cnt);
-		return cnt;
+		applogr(NULL, LOG_ERR, "%s: Failed to read device snString: %s", __func__, bfg_strerror(cnt, BST_LIBUSB));
 	}
 	
 	cnt = libztex_get_string_descriptor_ascii(newdev->hndl, newdev->descriptor.iProduct, buf, sizeof(buf));
 	if (unlikely(cnt < 0))
-		applog(LOG_WARNING, "Ztex check device: Failed to read device product with err %d", cnt);
+		applog(LOG_WARNING, "%s: Failed to read device product: %s", __func__, bfg_strerror(cnt, BST_LIBUSB));
 	else
 		newdev->dev_product = buf[0] ? strdup((void*)buf) : NULL;
 	
 	cnt = libztex_get_string_descriptor_ascii(newdev->hndl, newdev->descriptor.iManufacturer, buf, sizeof(buf));
 	if (unlikely(cnt < 0))
-		applog(LOG_WARNING, "Ztex check device: Failed to read device manufacturer with err %d", cnt);
+		applog(LOG_WARNING, "%s: Failed to read device manufacturer: %s", __func__, bfg_strerror(cnt, BST_LIBUSB));
 	else
 		newdev->dev_manufacturer = buf[0] ? strdup((void*)buf) : NULL;
 
 	cnt = libusb_control_transfer(newdev->hndl, 0xc0, 0x22, 0, 0, buf, 40, 500);
 	if (unlikely(cnt < 0)) {
-		applog(LOG_ERR, "Ztex check device: Failed to read ztex descriptor with err %d", cnt);
-		return cnt;
+		applogr(NULL, LOG_ERR, "%s: Failed to read ztex descriptor: %s", __func__, bfg_strerror(cnt, BST_LIBUSB));
 	}
 
 	if (buf[0] != 40 || buf[1] != 1 || buf[2] != 'Z' || buf[3] != 'T' || buf[4] != 'E' || buf[5] != 'X') {
-		applog(LOG_ERR, "Ztex check device: Error reading ztex descriptor");
-		return 2;
+		applogr(NULL, LOG_ERR, "%s: Unexpected data reading ztex descriptor", __func__);
 	}
 
 	newdev->productId[0] = buf[6];
@@ -665,16 +660,15 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 
 	cnt = libusb_control_transfer(newdev->hndl, 0xc0, 0x82, 0, 0, buf, 64, 500);
 	if (unlikely(cnt < 0)) {
-		applog(LOG_ERR, "Ztex check device: Failed to read ztex descriptor with err %d", cnt);
-		return cnt;
+		applogr(NULL, LOG_ERR, "%s: Failed to read ztex descriptor: %s", __func__, bfg_strerror(cnt, BST_LIBUSB));
 	}
 
 	if (unlikely(buf[0] != 5)) {
 		if (unlikely(buf[0] != 2 && buf[0] != 4)) {
-			applog(LOG_ERR, "Invalid BTCMiner descriptor version. Firmware must be updated (%d).", buf[0]);
-			return 3;
+			applogr(NULL, LOG_ERR, "%s: Invalid BTCMiner descriptor version (%d). Firmware must be updated.", __func__, buf[0]);
+			return NULL;
 		}
-		applog(LOG_WARNING, "Firmware out of date (%d).", buf[0]);
+		applog(LOG_WARNING, "%s: Firmware out of date (%d).", __func__, buf[0]);
 	}
 
 	i = buf[0] > 4? 11: (buf[0] > 2? 10: 8);
@@ -682,8 +676,7 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 	while (cnt < 64 && buf[cnt] != 0)
 		cnt++;
 	if (cnt < i + 1) {
-		applog(LOG_ERR, "Invalid bitstream file name .");
-		return 4;
+		applogr(NULL, LOG_ERR, "%s: Invalid bitstream file name.", __func__);
 	}
 
 	newdev->bitFileName = malloc(sizeof(char) * (cnt + 1));
@@ -700,20 +693,21 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 	newdev->hashesPerClock = buf[0] > 2? (((buf[8] & 255) | ((buf[9] & 255) << 8)) + 1) / 128.0: 1.0;
 	newdev->extraSolutions = buf[0] > 4? buf[10]: 0;
 
-	applog(LOG_DEBUG, "PID: %d numNonces: %d offsNonces: %d freqM1: %f freqMaxM: %d freqM: %d suspendSupported: %s hashesPerClock: %f extraSolutions: %d",
+	applog(LOG_DEBUG, "%s: PID: %d numNonces: %d offsNonces: %d freqM1: %f freqMaxM: %d freqM: %d suspendSupported: %s hashesPerClock: %f extraSolutions: %d",
+	       __func__,
 	                 buf[0], newdev->numNonces, newdev->offsNonces, newdev->freqM1, newdev->dclk.freqMaxM, newdev->dclk.freqM, newdev->suspendSupported ? "T": "F",
 	                 newdev->hashesPerClock, newdev->extraSolutions);
 
 	if (buf[0] < 4) {
 		if (strncmp(newdev->bitFileName, "ztex_ufm1_15b", 13) != 0)
 			newdev->hashesPerClock = 0.5;
-		applog(LOG_WARNING, "HASHES_PER_CLOCK not defined, assuming %0.2f", newdev->hashesPerClock);
+		applog(LOG_WARNING, "%s: HASHES_PER_CLOCK not defined, assuming %0.2f", __func__, newdev->hashesPerClock);
 	}
 
 	newdev->usbbus = libusb_get_bus_number(dev);
 	newdev->usbaddress = libusb_get_device_address(dev);
 	sprintf(newdev->repr, "ZTEX %s-1", newdev->snString);
-	return 0;
+	return newdev;
 }
 
 void libztex_destroy_device(struct libztex_device* ztex)
@@ -730,118 +724,6 @@ void libztex_destroy_device(struct libztex_device* ztex)
 	free(ztex);
 }
 
-int libztex_scanDevices(struct libztex_dev_list*** devs_p)
-{
-	int usbdevices[LIBZTEX_MAX_DESCRIPTORS];
-	struct libztex_dev_list **devs = NULL;
-	struct libztex_device *ztex = NULL;
-	int found, max_found = 0, pos = 0, err, rescan, ret = 0;
-	libusb_device **list = NULL;
-	ssize_t cnt, i;
-	int skipped = 0;
-
-	do {
-		cnt = libusb_get_device_list(NULL, &list);
-		if (unlikely(cnt < 0)) {
-			applog(LOG_ERR, "Ztex scan devices: Failed to list usb devices with err %"PRId64, (int64_t)cnt);
-			goto done;
-		}
-
-		for (found = rescan = i = 0; i < cnt; i++) {
-			if (bfg_claim_libusb(NULL, false, list[i]))
-			{
-				++skipped;
-				continue;
-			}
-			
-			err = libztex_checkDevice(list[i]);
-			switch (err) {
-			case CHECK_ERROR:
-				applog(LOG_ERR, "Ztex: Can not check device %ld", (long)i);
-				continue;
-			case CHECK_IS_NOT_ZTEX:
-				continue;
-			case CHECK_OK:
-				// Got one!
-				usbdevices[found++] = i;
-				break;
-			case CHECK_RESCAN:
-				rescan = 1;
-				found++;
-				break;
-			}
-		}
-
-		if (found < max_found)
-			rescan = 1;
-		else if (found > max_found)
-			max_found = found;
-
-		if (rescan)
-			libusb_free_device_list(list, 1);
-	} while (rescan);
-
-	if (0 == found)
-		goto done;
-
-	devs = malloc(sizeof(struct libztex_dev_list *) * found);
-	if (devs == NULL) {
-		applog(LOG_ERR, "Ztex scan devices: Failed to allocate memory");
-		goto done;
-	}
-
-	for (i = 0; i < found; i++) {
-		if (!ztex) {
-			ztex = malloc(sizeof(*ztex));
-			if (!ztex) {
-				applog(LOG_ERR, "%s: Can not allocate memory for device struct: %s", __func__, bfg_strerror(errno, BST_ERRNO));
-				goto done;
-			}
-		}
-
-		ztex->bitFileName = NULL;
-		ztex->numberOfFpgas = -1;
-
-		err = libztex_prepare_device(list[usbdevices[i]], &ztex);
-		if (unlikely(err != 0)) {
-			applog(LOG_ERR, "prepare device: %d", err);
-			libztex_destroy_device(ztex);
-			ztex = NULL;
-			continue;
-		}
-
-		devs[pos] = malloc(sizeof(struct libztex_dev_list));
-		if (NULL == devs[pos]) {
-			applog(LOG_ERR, "%s: Can not allocate memory for device: %s", __func__, bfg_strerror(errno, BST_ERRNO));
-			libztex_destroy_device(ztex);
-			ztex = NULL;
-			continue;
-		}
-
-		devs[pos]->dev = ztex;
-		ztex = NULL;
-		devs[pos]->next = NULL;
-		if (pos > 0)
-			devs[pos - 1]->next = devs[pos];
-		pos++;
-	}
-
-	ret = pos;
-
-done:
-	if (ret > 0)
-		*devs_p = devs;
-	else if (devs)
-		free(devs);
-	if (list)
-		libusb_free_device_list(list, 1);
-	
-	if (skipped)
-		applog(LOG_DEBUG, "%s: Skipping probe of %d claimed devices", __func__, skipped);
-	
-	return ret;
-}
-
 int libztex_sendHashData(struct libztex_device *ztex, unsigned char *sendbuf)
 {
 	int cnt = 0, ret, len;

+ 10 - 1
libztex.h

@@ -67,9 +67,18 @@ struct libztex_hash_data {
 	uint32_t hash7;
 };
 
+enum ztex_check_result
+{
+	CHECK_ERROR,
+	CHECK_IS_NOT_ZTEX,
+	CHECK_OK,
+	CHECK_RESCAN,
+};
+
 extern int libztex_scanDevices (struct libztex_dev_list ***devs);
 extern void libztex_freeDevList (struct libztex_dev_list **devs);
-extern int libztex_prepare_device (struct libusb_device *dev, struct libztex_device** ztex);
+extern enum ztex_check_result libztex_checkDevice(struct libusb_device *);
+extern struct libztex_device *libztex_prepare_device2(struct libusb_device *);
 extern void libztex_destroy_device (struct libztex_device* ztex);
 extern int libztex_configureFpga (struct libztex_device *dev, const char *repr);
 extern int libztex_setFreq (struct libztex_device *ztex, uint16_t freq, const char *repr);

+ 135 - 0
lowl-usb.c

@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012-2013 Luke Dashjr
+ *
+ * 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
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libusb.h>
+
+#include "fpgautils.h"
+#include "logging.h"
+#include "lowlevel.h"
+#include "miner.h"
+#include "util.h"
+
+static
+char *lowl_libusb_dup_string(libusb_device_handle * const handle, const uint8_t idx, const char * const idxname, const char * const fname)
+{
+	if (!idx)
+		return NULL;
+	unsigned char buf[0x100];
+	const int n = libusb_get_string_descriptor_ascii(handle, idx, buf, sizeof(buf)-1);
+	if (unlikely(n < 0)) {
+		applog(LOG_ERR, "%s: Error getting USB string %d (%s): %s",
+		       fname, idx, idxname, bfg_strerror(n, BST_LIBUSB));
+		return NULL;
+	}
+	if (n == 0)
+		return NULL;
+	buf[n] = '\0';
+	return strdup((void*)buf);
+}
+
+
+static
+void usb_devinfo_free(struct lowlevel_device_info * const info)
+{
+	libusb_device * const dev = info->lowl_data;
+	if (dev)
+		libusb_unref_device(dev);
+}
+
+static
+struct lowlevel_device_info *usb_devinfo_scan()
+{
+	struct lowlevel_device_info *devinfo_list = NULL;
+	ssize_t count, i;
+	libusb_device **list;
+	struct libusb_device_descriptor desc;
+	libusb_device_handle *handle;
+	struct lowlevel_device_info *info;
+	int err;
+
+	if (unlikely(!have_libusb))
+		return NULL;
+	
+	count = libusb_get_device_list(NULL, &list);
+	if (unlikely(count < 0)) {
+		applog(LOG_ERR, "%s: Error getting USB device list: %s",
+		       __func__, bfg_strerror(count, BST_LIBUSB));
+		return NULL;
+	}
+
+	for (i = 0; i < count; ++i) {
+		err = libusb_get_device_descriptor(list[i], &desc);
+		if (unlikely(err)) {
+			applog(LOG_ERR, "%s: Error getting device descriptor: %s",
+			       __func__, bfg_strerror(err, BST_LIBUSB));
+			continue;
+		}
+
+		info = malloc(sizeof(struct lowlevel_device_info));
+		*info = (struct lowlevel_device_info){
+			.lowl = &lowl_usb,
+			.devid = bfg_make_devid_libusb(list[i]),
+			.lowl_data = libusb_ref_device(list[i]),
+			.vid = desc.idVendor,
+			.pid = desc.idProduct,
+		};
+		
+		err = libusb_open(list[i], &handle);
+		if (unlikely(err))
+			applog(LOG_ERR, "%s: Error opening device: %s",
+			       __func__, bfg_strerror(err, BST_LIBUSB));
+		else
+		{
+			info->manufacturer = lowl_libusb_dup_string(handle, desc.iManufacturer, "iManufacturer", __func__);
+			info->product = lowl_libusb_dup_string(handle, desc.iProduct, "iProduct", __func__);
+			info->serial = lowl_libusb_dup_string(handle, desc.iSerialNumber, "iSerialNumber", __func__);
+			libusb_close(handle);
+		}
+
+		LL_PREPEND(devinfo_list, info);
+	}
+
+	libusb_free_device_list(list, 1);
+	
+	return devinfo_list;
+}
+
+struct libusb_device_handle *lowl_usb_open(struct lowlevel_device_info * const info)
+{
+	libusb_device * const dev = info->lowl_data;
+	
+	if (!dev)
+		return NULL;
+	
+	libusb_device_handle *devh;
+
+	if (libusb_open(dev, &devh)) {
+		applog(LOG_ERR, "%s: Error opening device", __func__);
+		return NULL;
+	}
+	return devh;
+}
+
+void lowl_usb_close(struct libusb_device_handle * const devh)
+{
+	libusb_close(devh);
+}
+
+struct lowlevel_driver lowl_usb = {
+	.dname = "usb",
+	.devinfo_scan = usb_devinfo_scan,
+	.devinfo_free = usb_devinfo_free,
+};

+ 77 - 13
lowlevel.c

@@ -9,15 +9,29 @@
 
 #include "config.h"
 
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <utlist.h>
 
+#include "logging.h"
 #include "lowlevel.h"
 
 static struct lowlevel_device_info *devinfo_list;
 
+void lowlevel_devinfo_semicpy(struct lowlevel_device_info * const dst, const struct lowlevel_device_info * const src)
+{
+#define COPYSTR(key)  BFGINIT(dst->key, maybe_strdup(src->key))
+	COPYSTR(manufacturer);
+	COPYSTR(product);
+	COPYSTR(serial);
+	COPYSTR(path);
+	COPYSTR(devid);
+	BFGINIT(dst->vid, src->vid);
+	BFGINIT(dst->pid, src->pid);
+}
+
 void lowlevel_devinfo_free(struct lowlevel_device_info * const info)
 {
 	if (info->lowl->devinfo_free)
@@ -26,6 +40,7 @@ void lowlevel_devinfo_free(struct lowlevel_device_info * const info)
 	free(info->product);
 	free(info->serial);
 	free(info->path);
+	free(info->devid);
 	free(info);
 }
 
@@ -49,6 +64,11 @@ void lowlevel_scan()
 	
 	lowlevel_scan_free();
 	
+#ifdef HAVE_LIBUSB
+	devinfo_mid_list = lowl_usb.devinfo_scan();
+	LL_CONCAT(devinfo_list, devinfo_mid_list);
+#endif
+	
 #ifdef USE_X6500
 	devinfo_mid_list = lowl_ft232r.devinfo_scan();
 	LL_CONCAT(devinfo_list, devinfo_mid_list);
@@ -58,26 +78,70 @@ void lowlevel_scan()
 	devinfo_mid_list = lowl_mcp2210.devinfo_scan();
 	LL_CONCAT(devinfo_list, devinfo_mid_list);
 #endif
+	
+#ifdef HAVE_FPGAUTILS
+	devinfo_mid_list = lowl_vcom.devinfo_scan();
+	LL_CONCAT(devinfo_list, devinfo_mid_list);
+#endif
+	
+	LL_FOREACH(devinfo_list, devinfo_mid_list)
+	{
+		applog(LOG_DEBUG, "%s: Found %s device at %s (path=%s, vid=%04x, pid=%04x, manuf=%s, prod=%s, serial=%s)",
+		       __func__,
+		       devinfo_mid_list->lowl->dname,
+		       devinfo_mid_list->devid,
+		       devinfo_mid_list->path,
+		       (unsigned)devinfo_mid_list->vid, (unsigned)devinfo_mid_list->pid,
+		       devinfo_mid_list->manufacturer, devinfo_mid_list->product, devinfo_mid_list->serial);
+	}
 }
 
-int _lowlevel_detect(lowl_found_devinfo_func_t cb, const char *serial, const char **product_needles)
+#define DETECT_BEGIN  \
+	struct lowlevel_device_info *info, *tmp;  \
+	int found = 0;  \
+	  \
+	LL_FOREACH_SAFE(devinfo_list, info, tmp)  \
+	{  \
+// END DETECT_BEGIN
+
+#define DETECT_PREEND  \
+		if (!cb(info, userp))  \
+			continue;  \
+		LL_DELETE(devinfo_list, info);  \
+		++found;  \
+// END DETECT_PREEND
+
+#define DETECT_END  \
+	}  \
+	return found;  \
+// END DETECT_END
+
+int _lowlevel_detect(lowl_found_devinfo_func_t cb, const char *serial, const char **product_needles, void * const userp)
 {
-	struct lowlevel_device_info *info, *tmp;
-	int found = 0, i;
+	int i;
 	
-	LL_FOREACH_SAFE(devinfo_list, info, tmp)
-	{
-		if (serial && strcmp(serial, info->serial))
+	DETECT_BEGIN
+		if (serial && ((!info->serial) || strcmp(serial, info->serial)))
+			continue;
+		if (product_needles[0] && !info->product)
 			continue;
 		for (i = 0; product_needles[i]; ++i)
 			if (!strstr(info->product, product_needles[i]))
 				goto next;
-		if (!cb(info))
-			continue;
-		LL_DELETE(devinfo_list, info);
-		++found;
+	DETECT_PREEND
 next: ;
-	}
-	
-	return found;
+	DETECT_END
+}
+
+int lowlevel_detect_id(const lowl_found_devinfo_func_t cb, void * const userp, const struct lowlevel_driver * const lowl, const int32_t vid, const int32_t pid)
+{
+	DETECT_BEGIN
+		if (info->lowl != lowl)
+			continue;
+		if (vid != -1 && vid != info->vid)
+			continue;
+		if (pid != -1 && pid != info->pid)
+			continue;
+	DETECT_PREEND
+	DETECT_END
 }

+ 20 - 4
lowlevel.h

@@ -2,12 +2,16 @@
 #define _BFG_LOWLEVEL_H
 
 #include <stdbool.h>
+#include <stdint.h>
+
+#include <uthash.h>
 
 struct lowlevel_device_info;
 
-typedef bool (*lowl_found_devinfo_func_t)(struct lowlevel_device_info *);
+typedef bool (*lowl_found_devinfo_func_t)(struct lowlevel_device_info *, void *);
 
 struct lowlevel_driver {
+	const char *dname;
 	struct lowlevel_device_info *(*devinfo_scan)();
 	void (*devinfo_free)(struct lowlevel_device_info *);
 };
@@ -17,18 +21,24 @@ struct lowlevel_device_info {
 	char *product;
 	char *serial;
 	char *path;
+	char *devid;
+	uint16_t vid;
+	uint16_t pid;
 	
 	struct lowlevel_driver *lowl;
 	void *lowl_data;
 	
 	struct lowlevel_device_info *next;
+	UT_hash_handle hh;
 };
 
 extern void lowlevel_scan();
-extern int _lowlevel_detect(lowl_found_devinfo_func_t, const char *serial, const char **product_needles);
-#define lowlevel_detect(func, ...)  _lowlevel_detect(func, NULL, (const char *[]){__VA_ARGS__, NULL})
-#define lowlevel_detect_serial(func, serial)  _lowlevel_detect(func, serial, (const char *[]){NULL})
+extern int _lowlevel_detect(lowl_found_devinfo_func_t, const char *serial, const char **product_needles, void *);
+#define lowlevel_detect(func, ...)  _lowlevel_detect(func, NULL, (const char *[]){__VA_ARGS__, NULL}, NULL)
+#define lowlevel_detect_serial(func, serial)  _lowlevel_detect(func, serial, (const char *[]){NULL}, NULL)
+extern int lowlevel_detect_id(lowl_found_devinfo_func_t, void *, const struct lowlevel_driver *, int32_t vid, int32_t pid);
 extern void lowlevel_scan_free();
+extern void lowlevel_devinfo_semicpy(struct lowlevel_device_info *dst, const struct lowlevel_device_info *src);
 extern void lowlevel_devinfo_free(struct lowlevel_device_info *);
 
 #ifdef USE_X6500
@@ -37,5 +47,11 @@ extern struct lowlevel_driver lowl_ft232r;
 #ifdef USE_NANOFURY
 extern struct lowlevel_driver lowl_mcp2210;
 #endif
+#ifdef HAVE_LIBUSB
+extern struct lowlevel_driver lowl_usb;
+#endif
+#ifdef HAVE_FPGAUTILS
+extern struct lowlevel_driver lowl_vcom;
+#endif
 
 #endif

+ 4 - 0
mcp2210.c

@@ -174,9 +174,12 @@ struct lowlevel_device_info *mcp2210_devinfo_scan()
 	LL_FOREACH(hid_enum, hid_item)
 	{
 		info = malloc(sizeof(struct lowlevel_device_info));
+		char * const devid = malloc(4 + strlen(hid_item->path) + 1);
+		sprintf(devid, "hid:%s", hid_item->path);
 		*info = (struct lowlevel_device_info){
 			.lowl = &lowl_mcp2210,
 			.path = strdup(hid_item->path),
+			.devid = devid,
 			.manufacturer = wcs2str_dup(hid_item->manufacturer_string),
 			.product = wcs2str_dup(hid_item->product_string),
 			.serial  = wcs2str_dup(hid_item->serial_number),
@@ -449,5 +452,6 @@ enum mcp2210_gpio_value mcp2210_get_gpio_input(struct mcp2210_device * const h,
 }
 
 struct lowlevel_driver lowl_mcp2210 = {
+	.dname = "mcp2210",
 	.devinfo_scan = mcp2210_devinfo_scan,
 };

+ 45 - 204
miner.c

@@ -155,7 +155,6 @@ unsigned long global_quota_gcd = 1;
 int opt_dynamic_interval = 7;
 int nDevs;
 int opt_g_threads = -1;
-int gpu_threads;
 #endif
 #ifdef USE_SCRYPT
 static char detect_algo = 1;
@@ -164,7 +163,6 @@ bool opt_scrypt;
 static char detect_algo;
 #endif
 bool opt_restart = true;
-static bool opt_nogpu;
 
 #ifdef USE_LIBMICROHTTPD
 #include "httpsrv.h"
@@ -889,58 +887,23 @@ char *set_request_diff(const char *arg, float *p)
 	return NULL;
 }
 
-#ifdef HAVE_LIBUDEV
-#include <libudev.h>
-#endif
-
-static
-char *add_serial_all(const char *arg, const char *p) {
-	size_t pLen = p - arg;
-	char dev[pLen + PATH_MAX];
-	memcpy(dev, arg, pLen);
-	char *devp = &dev[pLen];
-
-#ifdef HAVE_LIBUDEV
-
-	struct udev *udev = udev_new();
-	struct udev_enumerate *enumerate = udev_enumerate_new(udev);
-	struct udev_list_entry *list_entry;
-
-	udev_enumerate_add_match_subsystem(enumerate, "tty");
-	udev_enumerate_add_match_property(enumerate, "ID_SERIAL", "*");
-	udev_enumerate_scan_devices(enumerate);
-	udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) {
-		struct udev_device *device = udev_device_new_from_syspath(
-			udev_enumerate_get_udev(enumerate),
-			udev_list_entry_get_name(list_entry)
-		);
-		if (!device)
-			continue;
-
-		const char *devpath = udev_device_get_devnode(device);
-		if (devpath) {
-			strcpy(devp, devpath);
-			applog(LOG_DEBUG, "scan-serial: libudev all-adding %s", dev);
-			string_elist_add(dev, &scan_devices);
-		}
-
-		udev_device_unref(device);
-	}
-	udev_enumerate_unref(enumerate);
-	udev_unref(udev);
-
-#elif defined(WIN32)
+extern struct lowlevel_device_info *_vcom_devinfo_findorcreate(struct lowlevel_device_info **, const char *);
 
+#ifdef WIN32
+void _vcom_devinfo_scan_querydosdevice(struct lowlevel_device_info ** const devinfo_list)
+{
+	char dev[PATH_MAX];
+	char *devp = dev;
 	size_t bufLen = 0x100;
 tryagain: ;
 	char buf[bufLen];
 	if (!QueryDosDevice(NULL, buf, bufLen)) {
 		if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
 			bufLen *= 2;
-			applog(LOG_DEBUG, "scan-serial: QueryDosDevice returned insufficent buffer error; enlarging to %lx", (unsigned long)bufLen);
+			applog(LOG_DEBUG, "QueryDosDevice returned insufficent buffer error; enlarging to %lx", (unsigned long)bufLen);
 			goto tryagain;
 		}
-		return "scan-serial: Error occurred trying to enumerate COM ports with QueryDosDevice";
+		applog(LOG_WARNING, "Error occurred trying to enumerate COM ports with QueryDosDevice");
 	}
 	size_t tLen;
 	memcpy(devp, "\\\\.\\", 4);
@@ -950,12 +913,14 @@ tryagain: ;
 		if (strncmp("COM", t, 3))
 			continue;
 		memcpy(devp, t, tLen);
-		applog(LOG_DEBUG, "scan-serial: QueryDosDevice all-adding %s", dev);
-		string_elist_add(dev, &scan_devices);
+		_vcom_devinfo_findorcreate(devinfo_list, dev);
 	}
-
+}
 #else
-
+void _vcom_devinfo_scan_lsdev(struct lowlevel_device_info ** const devinfo_list)
+{
+	char dev[PATH_MAX];
+	char *devp = dev;
 	DIR *D;
 	struct dirent *de;
 	const char devdir[] = "/dev";
@@ -965,7 +930,7 @@ tryagain: ;
 	
 	D = opendir(devdir);
 	if (!D)
-		return "scan-serial 'all' is not supported on this platform";
+		applogr(, LOG_DEBUG, "No /dev directory to look for VCOM devices in");
 	memcpy(devpath, devdir, devdirlen);
 	devpath[devdirlen] = '/';
 	while ( (de = readdir(D)) ) {
@@ -978,30 +943,21 @@ tryagain: ;
 		
 trydev:
 		strcpy(devfile, de->d_name);
-		applog(LOG_DEBUG, "scan-serial: /dev glob all-adding %s", dev);
-		string_elist_add(dev, &scan_devices);
+		_vcom_devinfo_findorcreate(devinfo_list, dev);
 	}
 	closedir(D);
-	
-	return NULL;
-
+}
 #endif
 
+static char *add_serial(const char *arg)
+{
+	string_elist_add(arg, &scan_devices);
 	return NULL;
 }
 
-static char *add_serial(const char *arg)
+static char *compat_disable_gpu(__maybe_unused void *arg)
 {
-	const char *p = strchr(arg, ':');
-	if (p)
-		++p;
-	else
-		p = arg;
-	if (!strcasecmp(p, "all")) {
-		return add_serial_all(arg, p);
-	}
-
-	string_elist_add(arg, &scan_devices);
+	string_elist_add("opencl:noauto", &scan_devices);
 	return NULL;
 }
 
@@ -1745,7 +1701,7 @@ static struct opt_table opt_config_table[] = {
 		     set_devices, NULL, NULL,
 	             "Select device to use, one value, range and/or comma separated (e.g. 0-2,4) default: all"),
 	OPT_WITHOUT_ARG("--disable-gpu|-G",
-			opt_set_bool, &opt_nogpu,
+			compat_disable_gpu, NULL,
 			opt_hidden
 	),
 	OPT_WITHOUT_ARG("--disable-rejecting",
@@ -10101,60 +10057,6 @@ struct device_drv cpu_drv = {
 };
 #endif
 
-#ifdef USE_BITFORCE
-extern struct device_drv bitforce_drv;
-#endif
-
-#ifdef USE_BIGPIC
-extern struct device_drv bigpic_drv;
-#endif
-
-#ifdef USE_ICARUS
-extern struct device_drv cairnsmore_drv;
-extern struct device_drv erupter_drv;
-extern struct device_drv icarus_drv;
-#endif
-
-#ifdef USE_AVALON
-extern struct device_drv avalon_drv;
-#endif
-
-#ifdef USE_KNC
-extern struct device_drv knc_drv;
-#endif
-
-#ifdef USE_LITTLEFURY
-extern struct device_drv littlefury_drv;
-#endif
-
-#ifdef USE_MODMINER
-extern struct device_drv modminer_drv;
-#endif
-
-#ifdef USE_NANOFURY
-extern struct device_drv nanofury_drv;
-#endif
-
-#ifdef USE_X6500
-extern struct device_drv x6500_api;
-#endif
-
-#ifdef USE_ZTEX
-extern struct device_drv ztex_drv;
-#endif
-
-#ifdef USE_BITFURY
-extern struct device_drv bitfury_drv;
-#endif
-
-#ifdef USE_METABANK
-extern struct device_drv metabank_drv;
-#endif
-
-#ifdef USE_BFSB
-extern struct device_drv bfsb_drv;
-#endif
-
 static int cgminer_id_count = 0;
 static int device_line_id_count;
 
@@ -10172,11 +10074,6 @@ void register_device(struct cgpu_info *cgpu)
 #ifdef HAVE_CURSES
 	adj_width(mining_threads, &dev_width);
 #endif
-#ifdef HAVE_OPENCL
-	if (cgpu->drv == &opencl_api) {
-		gpu_threads += cgpu->threads;
-	}
-#endif
 
 	rwlock_init(&cgpu->qlock);
 	cgpu->queued_work = NULL;
@@ -10215,95 +10112,39 @@ extern void setup_pthread_cancel_workaround();
 extern struct sigaction pcwm_orig_term_handler;
 #endif
 
+bool bfg_need_detect_rescan;
+
 static
 void drv_detect_all()
 {
+rescan:
+	bfg_need_detect_rescan = false;
+	
 #ifdef HAVE_BFG_LOWLEVEL
 	lowlevel_scan();
 #endif
-
-#ifdef USE_ICARUS
-	if (!opt_scrypt)
-	{
-		cairnsmore_drv.drv_detect();
-		erupter_drv.drv_detect();
-		icarus_drv.drv_detect();
-	}
-#endif
-
-#ifdef USE_BITFORCE
-	if (!opt_scrypt)
-		bitforce_drv.drv_detect();
-#endif
-
-#ifdef USE_BIGPIC
-	if (!opt_scrypt)
-		bigpic_drv.drv_detect();
-#endif
-
-#ifdef USE_KNC
-	if (!opt_scrypt)
-		knc_drv.drv_detect();
-#endif
-
-#ifdef USE_MODMINER
-	if (!opt_scrypt)
-		modminer_drv.drv_detect();
-#endif
-
-#ifdef USE_NANOFURY
-	if (!opt_scrypt)
-		nanofury_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
-
-#ifdef USE_BITFURY
-	if (!opt_scrypt)
+	
+	struct driver_registration *reg, *tmp;
+	const int algomatch = opt_scrypt ? POW_SCRYPT : POW_SHA256D;
+	BFG_FOREACH_DRIVER_BY_PRIORITY(reg, tmp)
 	{
-		bitfury_drv.drv_detect();
-#ifdef USE_METABANK
-		metabank_drv.drv_detect();
-#endif
-#ifdef USE_BFSB
-		bfsb_drv.drv_detect();
-#endif
+		const struct device_drv * const drv = reg->drv;
+		const supported_algos_t algos = drv->supported_algos ?: POW_SHA256D;
+		if (0 == (algos & algomatch) || !drv->drv_detect)
+			continue;
+		
+		drv->drv_detect();
 	}
-#endif
-
-#ifdef USE_LITTLEFURY
-	if (!opt_scrypt)
-		littlefury_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 HAVE_BFG_LOWLEVEL
 	lowlevel_scan_free();
 #endif
-
-#ifdef HAVE_OPENCL
-	if (!opt_nogpu)
-		opencl_api.drv_detect();
-	gpu_threads = 0;
-#endif
+	
+	if (bfg_need_detect_rescan)
+	{
+		applog(LOG_DEBUG, "Device rescan requested");
+		goto rescan;
+	}
 }
 
 static
@@ -10829,6 +10670,7 @@ int main(int argc, char *argv[])
 	}
 #endif
 
+	bfg_devapi_init();
 	drv_detect_all();
 	total_devices = total_devices_new;
 	devices = devices_new;
@@ -11065,7 +10907,6 @@ begin_bench:
 	}
 
 #ifdef HAVE_OPENCL
-	applog(LOG_INFO, "%d gpu miner threads started", gpu_threads);
 	for (i = 0; i < nDevs; i++)
 		pause_dynamic_threads(i);
 #endif

+ 9 - 1
miner.h

@@ -283,6 +283,12 @@ struct gpu_adl {
 };
 #endif
 
+enum pow_algorithm {
+	POW_SHA256D = 1,
+	POW_SCRYPT  = 2,
+};
+typedef uint8_t supported_algos_t;
+
 struct api_data;
 struct thr_info;
 struct work;
@@ -290,8 +296,11 @@ struct work;
 struct device_drv {
 	const char *dname;
 	const char *name;
+	int8_t probe_priority;
+	supported_algos_t supported_algos;
 
 	// DRV-global functions
+	void (*drv_init)();
 	void (*drv_detect)();
 
 	// Processor-specific functions
@@ -1066,7 +1075,6 @@ extern bool opt_quiet;
 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
 extern bool opt_scrypt;
 #else

+ 5 - 0
util.h

@@ -116,6 +116,11 @@ extern char *absolute_uri(char *uri, const char *ref);  // ref must be a root UR
 extern void ucs2tochar(char *out, const uint16_t *in, size_t sz);
 extern char *ucs2tochar_dup(uint16_t *in, size_t sz);
 
+#define BFGINIT(var, val)  do{  \
+	if (!(var))       \
+		(var) = val;  \
+}while(0)
+
 extern void gen_hash(unsigned char *data, unsigned char *hash, int len);
 extern void hash_data(unsigned char *out_hash, const unsigned char *data);
 extern void real_block_target(unsigned char *target, const unsigned char *data);