Browse Source

USB add --usb options to limit USB device selection v0.1

Kano 13 years ago
parent
commit
3f50e878a2
5 changed files with 278 additions and 14 deletions
  1. 50 3
      README
  2. 25 4
      cgminer.c
  3. 5 3
      miner.h
  4. 197 4
      usbutils.c
  5. 1 0
      usbutils.h

+ 50 - 3
README

@@ -221,10 +221,57 @@ SCRYPT only options:
 See SCRYPT-README for more information regarding litecoin mining.
 
 
-FPGA mining boards (BitForce, Icarus, ModMiner, Ztex) only options:
+ASIC and FPGA mining boards (BFL ASIC, BitForce, Icarus, ModMiner, Ztex)
+only options:
+
+Cgminer will automatically find all of your BFL ASIC, BitForce FPGAs,
+ModMiner FPGAs or Ztex FPGAs
+The --usb option can restrict how many BFL ASIC, BitForce FPGAs or
+ModMiner FPGAs it finds:
+
+  --usb 1:2,1:3,1:4,1:*
+or
+  --usb BAS:1,BFL:1,MMQ:0
+or
+  --usb :10
+
+You can only use one of the above 3
+
+The first version
+  --usb 1:2,1:3,1:4,1:*
+allows you to select which devices to mine on with a list of USB
+ bus_number:device_address
+All other USB devices will be ignored
+Hotplug will also only look at the devices matching the list specified and
+find nothing new if they are all in use
+You can specify just the USB bus_number to find all devices like 1:*
+which means any devices on USB bus_number 1
+This is useful if you unplug a device then plug it back in the same port,
+it usually reappears with the same bus_number but a different device_address
+
+You can see the list of USB devices on linux with 'sudo lsusb'
+Cgminer will list the USB devices with the '--usb-dump 1' option
+The '--usb-dump N' option with a value of N greater than 1 will dump a lot
+of details about each USB device
+
+The second version
+  --usb BAS:1,BFL:1,MMQ:0
+allows you to specify how many devices to choose based on each device
+driver cgminer has - there are currently 3 USB drivers: BAS, BFL & MMQ
+N.B. you can only specify which device driver to limit, not the type of
+each device, e.g. with BAS:n you can limit how many BFL ASIC devices will
+be checked, but you cannot limit the number of each type of BFL ASIC
+Also note that the MMQ count is the number of MMQ backplanes you have
+not the number of MMQ FPGAs
+
+The third version
+  --usb :10
+means only use a maximum of 10 devices of any supported USB devices
+Once cgminer has 10 devices it will not configure any more and hotplug will
+not scan for any more
+If one of the 10 devices stops working, hotplug - if enabled, as is default
+- will scan normally again until it has 10 devices
 
-cgminer will automatically find your ModMiner, BitForce or Ztex FPGAs
-independent of the --scan-serial options specified below
 
 --scan-serial|-S <arg> Serial port to probe for Icarus mining device
 

+ 25 - 4
cgminer.c

@@ -136,7 +136,8 @@ bool opt_disable_pool;
 char *opt_icarus_options = NULL;
 char *opt_icarus_timing = NULL;
 bool opt_worktime;
-#ifdef HAVE_LIBUSB
+#ifdef USE_USBUTILS
+char *opt_usb_select = NULL;
 int opt_usbdump = -1;
 #endif
 
@@ -169,7 +170,7 @@ static int new_threads;
 static int start_devices;
 int hotplug_time = 5;
 
-#ifdef HAVE_LIBUSB
+#ifdef USE_USBUTILS
 pthread_mutex_t cgusb_lock;
 #endif
 
@@ -823,6 +824,15 @@ static char *set_icarus_timing(const char *arg)
 }
 #endif
 
+#ifdef USE_USBUTILS
+static char *set_usb_select(const char *arg)
+{
+	opt_set_charp(arg, &opt_usb_select);
+
+	return NULL;
+}
+#endif
+
 static char *set_null(const char __maybe_unused *arg)
 {
 	return NULL;
@@ -1154,7 +1164,10 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITH_ARG("--user|-u",
 		     set_user, NULL, NULL,
 		     "Username for bitcoin JSON-RPC server"),
-#ifdef HAVE_LIBUSB
+#ifdef USE_USBUTILS
+	OPT_WITH_ARG("--usb",
+		     set_usb_select, NULL, NULL,
+		     "USB device selection"),
 	OPT_WITH_ARG("--usb-dump",
 		     set_int_0_to_10, opt_show_intval, &opt_usbdump,
 		     opt_hidden),
@@ -4088,6 +4101,10 @@ void write_config(FILE *fcfg)
 		fprintf(fcfg, ",\n\"icarus-options\" : \"%s\"", json_escape(opt_icarus_options));
 	if (opt_icarus_timing)
 		fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", json_escape(opt_icarus_timing));
+#ifdef USE_USBUTILS
+	if (opt_usb_select)
+		fprintf(fcfg, ",\n\"usb\" : \"%s\"", json_escape(opt_usb_select));
+#endif
 	fputs("\n}\n", fcfg);
 
 	json_escape_free();
@@ -7031,7 +7048,6 @@ int main(int argc, char *argv[])
 		fflush(stderr);
 		quit(1, "libusb_init() failed");
 	}
-	mutex_init(&cgusb_lock);
 #endif
 
 	mutex_init(&hash_lock);
@@ -7119,6 +7135,11 @@ int main(int argc, char *argv[])
 	if (argc != 1)
 		quit(1, "Unexpected extra commandline arguments");
 
+#ifdef USE_USBUTILS
+	mutex_init(&cgusb_lock);
+	usb_initialise();
+#endif
+
 	if (!config_loaded)
 		load_default_config();
 

+ 5 - 3
miner.h

@@ -197,13 +197,14 @@ static inline int fsync (int fd)
 #endif
 
 enum drv_driver {
-	DRIVER_OPENCL,
+	DRIVER_OPENCL = 0,
 	DRIVER_ICARUS,
 	DRIVER_BITFORCE,
 	DRIVER_MODMINER,
 	DRIVER_ZTEX,
 	DRIVER_CPU,
 	DRIVER_BFLSC,
+	DRIVER_MAX
 };
 
 enum alive {
@@ -718,7 +719,8 @@ extern bool opt_restart;
 extern char *opt_icarus_options;
 extern char *opt_icarus_timing;
 extern bool opt_worktime;
-#ifdef HAVE_LIBUSB
+#ifdef USE_USBUTILS
+extern char *opt_usb_select;
 extern int opt_usbdump;
 #endif
 #ifdef USE_BITFORCE
@@ -751,7 +753,7 @@ extern int opt_queue;
 extern int opt_scantime;
 extern int opt_expiry;
 
-#ifdef HAVE_LIBUSB
+#ifdef USE_USBUTILS
 extern pthread_mutex_t cgusb_lock;
 #endif
 

+ 197 - 4
usbutils.c

@@ -9,6 +9,7 @@
 
 #include "config.h"
 
+#include <ctype.h>
 #include <stdint.h>
 #include <stdbool.h>
 
@@ -146,6 +147,24 @@ extern struct device_drv icarus_drv;
 #define STRBUFLEN 256
 static const char *BLANK = "";
 
+// For device limits by driver
+static struct driver_count {
+	uint32_t count;
+	uint32_t limit;
+} drv_count[DRIVER_MAX];
+
+// For device limits by list of bus/dev
+static struct usb_busdev {
+	int bus_number;
+	int device_address;
+} *busdev;
+
+static int busdev_count = 0;
+
+// Total device limit
+static int total_count = 0;
+static int total_limit = 999999;
+
 static bool stats_initialised = false;
 
 struct cg_usb_stats_item {
@@ -894,6 +913,9 @@ static void release_cgpu(struct cgpu_info *cgpu)
 	if (cgpu->usbinfo.nodev)
 		return;
 
+	total_count--;
+	drv_count[cgpu->drv->drv_id].count--;
+
 	cgpu->usbinfo.nodev = true;
 	cgpu->usbinfo.nodev_count++;
 	gettimeofday(&(cgpu->usbinfo.last_nodev), NULL);
@@ -903,6 +925,9 @@ static void release_cgpu(struct cgpu_info *cgpu)
 	for (i = 0; i < total_devices; i++) {
 		lookcgpu = get_devices(i);
 		if (lookcgpu != cgpu && lookcgpu->usbdev == cgusb) {
+			total_count--;
+			drv_count[lookcgpu->drv->drv_id].count--;
+
 			lookcgpu->usbinfo.nodev = true;
 			lookcgpu->usbinfo.nodev_count++;
 			memcpy(&(lookcgpu->usbinfo.last_nodev),
@@ -1121,7 +1146,9 @@ dame:
 static bool usb_check_device(struct device_drv *drv, struct libusb_device *dev, struct usb_find_devices *look)
 {
 	struct libusb_device_descriptor desc;
-	int err;
+	int bus_number, device_address;
+	int err, i;
+	bool ok;
 
 	err = libusb_get_device_descriptor(dev, &desc);
 	if (err) {
@@ -1136,6 +1163,27 @@ static bool usb_check_device(struct device_drv *drv, struct libusb_device *dev,
 		return false;
 	}
 
+	if (busdev_count > 0) {
+		bus_number = (int)libusb_get_bus_number(dev);
+		device_address = (int)libusb_get_device_address(dev);
+		ok = false;
+		for (i = 0; i < busdev_count; i++) {
+			if (bus_number == busdev[i].bus_number) {
+				if (busdev[i].device_address == -1 ||
+				    device_address == busdev[i].device_address) {
+					ok = true;
+					break;
+				}
+			}
+		}
+		if (!ok) {
+			applog(LOG_DEBUG, "%s rejected %s %04x:%04x with bus:dev (%d:%d)",
+				drv->name, look->name, look->idVendor, look->idProduct,
+				bus_number, device_address);
+			return false;
+		}
+	}
+
 	applog(LOG_DEBUG, "%s looking for and found %s %04x:%04x",
 		drv->name, look->name, look->idVendor, look->idProduct);
 
@@ -1161,6 +1209,13 @@ static struct usb_find_devices *usb_check_each(int drvnum, struct device_drv *dr
 
 static struct usb_find_devices *usb_check(__maybe_unused struct device_drv *drv, __maybe_unused struct libusb_device *dev)
 {
+	if (drv_count[drv->drv_id].count >= drv_count[drv->drv_id].limit) {
+		applog(LOG_DEBUG,
+			"USB scan devices3: %s limit %d reached",
+			drv->dname, drv_count[drv->drv_id].limit);
+		return NULL;
+	}
+
 #ifdef USE_BFLSC
 	if (drv->drv_id == DRIVER_BFLSC)
 		return usb_check_each(DRV_BFLSC, drv, dev);
@@ -1190,7 +1245,19 @@ void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_devi
 	ssize_t count, i;
 	struct usb_find_devices *found;
 
-	cgusb_check_init();
+	applog(LOG_DEBUG, "USB scan devices: checking for %s devices", drv->name);
+
+	if (total_count >= total_limit) {
+		applog(LOG_DEBUG, "USB scan devices: total limit %d reached", total_limit);
+		return;
+	}
+
+	if (drv_count[drv->drv_id].count >= drv_count[drv->drv_id].limit) {
+		applog(LOG_DEBUG,
+			"USB scan devices: %s limit %d reached",
+			drv->dname, drv_count[drv->drv_id].limit);
+		return;
+	}
 
 	count = libusb_get_device_list(NULL, &list);
 	if (count < 0) {
@@ -1202,6 +1269,18 @@ void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_devi
 		applog(LOG_DEBUG, "USB scan devices: found no devices");
 
 	for (i = 0; i < count; i++) {
+		if (total_count >= total_limit) {
+			applog(LOG_DEBUG, "USB scan devices2: total limit %d reached", total_limit);
+			break;
+		}
+
+		if (drv_count[drv->drv_id].count >= drv_count[drv->drv_id].limit) {
+			applog(LOG_DEBUG,
+				"USB scan devices2: %s limit %d reached",
+				drv->dname, drv_count[drv->drv_id].limit);
+			break;
+		}
+
 		found = usb_check(drv, list[i]);
 		if (found != NULL) {
 			if (cgminer_usb_lock(drv, list[i]) == false)
@@ -1209,6 +1288,10 @@ void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_devi
 			else {
 				if (!device_detect(list[i], found))
 					cgminer_usb_unlock(drv, list[i]);
+				else {
+					total_count++;
+					drv_count[drv->drv_id].count++;
+				}
 			}
 		}
 	}
@@ -1240,8 +1323,6 @@ struct api_data *api_usb_stats(__maybe_unused int *count)
 	int device;
 	int cmdseq;
 
-	cgusb_check_init();
-
 	if (next_stat == 0)
 		return NULL;
 
@@ -1312,9 +1393,12 @@ static void newstats(struct cgpu_info *cgpu)
 {
 	int i;
 
+	mutex_lock(&cgusb_lock);
 	cgpu->usbinfo.usbstat = ++next_stat;
+	mutex_unlock(&cgusb_lock);
 
 	usb_stats = realloc(usb_stats, sizeof(*usb_stats) * next_stat);
+
 	usb_stats[next_stat-1].name = cgpu->drv->name;
 	usb_stats[next_stat-1].device_id = -1;
 	usb_stats[next_stat-1].details = calloc(1, sizeof(struct cg_usb_stats_details) * C_MAX * 2);
@@ -1584,3 +1668,112 @@ void usb_cleanup()
 		}
 	}
 }
+
+void usb_initialise()
+{
+	char *fre, *ptr, *comma, *colon;
+	int bus, dev, lim, i;
+	bool found;
+
+	for (i = 0; i < DRIVER_MAX; i++) {
+		drv_count[i].count = 0;
+		drv_count[i].limit = 999999;
+	}
+
+	cgusb_check_init();
+
+	if (opt_usb_select && *opt_usb_select) {
+		// Absolute device limit
+		if (*opt_usb_select == ':') {
+			total_limit = atoi(opt_usb_select+1);
+			if (total_limit < 0)
+				quit(1, "Invalid --usb total limit");
+		// Comma list of bus:dev devices to match
+		} else if (isdigit(*opt_usb_select)) {
+			fre = ptr = strdup(opt_usb_select);
+			do {
+				comma = strchr(ptr, ',');
+				if (comma)
+					*(comma++) = '\0';
+
+				colon = strchr(ptr, ':');
+				if (!colon)
+					quit(1, "Invalid --usb bus:dev missing ':'");
+
+				*(colon++) = '\0';
+
+				if (!isdigit(*ptr))
+					quit(1, "Invalid --usb bus:dev - bus must be a number");
+
+				if (!isdigit(*colon) && *colon != '*')
+					quit(1, "Invalid --usb bus:dev - dev must be a number or '*'");
+
+				bus = atoi(ptr);
+				if (bus <= 0)
+					quit(1, "Invalid --usb bus:dev - bus must be > 0");
+
+				if (!colon == '*')
+					dev = -1;
+				else {
+					dev = atoi(colon);
+					if (dev <= 0)
+						quit(1, "Invalid --usb bus:dev - dev must be > 0 or '*'");
+				}
+
+				busdev = realloc(busdev, sizeof(*busdev) * (++busdev_count));
+
+				busdev[busdev_count-1].bus_number = bus;
+				busdev[busdev_count-1].device_address = dev;
+
+				ptr = comma;
+			} while (ptr);
+			free(fre);
+		// Comma list of DRV:limit
+		} else {
+			fre = ptr = strdup(opt_usb_select);
+			do {
+				comma = strchr(ptr, ',');
+				if (comma)
+					*(comma++) = '\0';
+
+				colon = strchr(ptr, ':');
+				if (!colon)
+					quit(1, "Invalid --usb DRV:limit missing ':'");
+
+				*(colon++) = '\0';
+
+				if (!isdigit(*colon))
+					quit(1, "Invalid --usb DRV:limit - limit must be a number");
+
+				lim = atoi(colon);
+				if (lim < 0)
+					quit(1, "Invalid --usb DRV:limit - limit must be >= 0");
+
+				found = false;
+#ifdef USE_BFLSC
+				if (strcasecmp(ptr, bflsc_drv.name) == 0) {
+					drv_count[bflsrc_drv.drv_id].limit = lim;
+					found = true;
+				}
+#endif
+#ifdef USE_BITFORCE
+				if (!found && strcasecmp(ptr, bitforce_drv.name) == 0) {
+					drv_count[bitforce_drv.drv_id].limit = lim;
+					found = true;
+				}
+#endif
+#ifdef USE_MODMINER
+				if (!found && strcasecmp(ptr, modminer_drv.name) == 0) {
+					drv_count[modminer_drv.drv_id].limit = lim;
+					found = true;
+				}
+#endif
+				if (!found)
+					quit(1, "Invalid --usb DRV:limit - unknown DRV='%s'", ptr);
+
+				ptr = comma;
+			} while (ptr);
+			free(fre);
+		}
+	}
+}

+ 1 - 0
usbutils.h

@@ -138,6 +138,7 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds);
 int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout, enum usb_cmds cmd);
 void usb_cleanup();
+void usb_initialise();
 
 #define usb_read(cgpu, buf, bufsiz, read, cmd) \
 	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, -1, cmd, false)