diff --git a/README b/README index 523df8f9..29b31052 100644 --- a/README +++ b/README @@ -224,10 +224,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 0' option +The '--usb-dump N' option with a value of N greater than 0 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 Serial port to probe for Icarus mining device diff --git a/cgminer.c b/cgminer.c index b3845955..7736cfdf 100644 --- a/cgminer.c +++ b/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 @@ -826,6 +827,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; @@ -1157,7 +1167,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), @@ -4087,6 +4100,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(); @@ -7034,7 +7051,9 @@ int main(int argc, char *argv[]) fflush(stderr); quit(1, "libusb_init() failed"); } +#ifdef USE_USBUTILS mutex_init(&cgusb_lock); +#endif #endif mutex_init(&hash_lock); @@ -7170,6 +7189,10 @@ int main(int argc, char *argv[]) if (want_per_device_stats) opt_log_output = true; +#ifdef USE_USBUTILS + usb_initialise(); +#endif + #ifdef WANT_CPUMINE #ifdef USE_SCRYPT if (opt_scrypt) diff --git a/miner.h b/miner.h index 08c18fdf..5581316d 100644 --- a/miner.h +++ b/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 { @@ -777,7 +778,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 @@ -810,7 +812,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 diff --git a/usbutils.c b/usbutils.c index 7aed305e..93d918ca 100644 --- a/usbutils.c +++ b/usbutils.c @@ -9,6 +9,7 @@ #include "config.h" +#include #include #include @@ -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 { @@ -395,6 +414,8 @@ static bool setgetdes(ssize_t count, libusb_device *dev, struct libusb_device_ha static void usb_full(ssize_t count, libusb_device *dev, char **buf, size_t *off, size_t *len) { struct libusb_device_descriptor desc; + uint8_t bus_number; + uint8_t device_address; struct libusb_device_handle *handle; struct libusb_config_descriptor *config; const struct libusb_interface_descriptor *idesc; @@ -413,16 +434,26 @@ static void usb_full(ssize_t count, libusb_device *dev, char **buf, size_t *off, return; } - sprintf(tmp, EOL ".USB dev %d: Device Descriptor:" EOL "\tLength: %d" EOL + bus_number = libusb_get_bus_number(dev); + device_address = libusb_get_device_address(dev); + + if (opt_usbdump == 0) { + sprintf(tmp, EOL ".USB dev %d: Bus %d Device %d ID: %04x:%04x", + (int)count, (int)bus_number, (int)device_address, + desc.idVendor, desc.idProduct); + } else { + sprintf(tmp, EOL ".USB dev %d: Bus %d Device %d Device Descriptor:" EOL "\tLength: %d" EOL "\tDescriptor Type: %s" EOL "\tUSB: %04x" EOL "\tDeviceClass: %d" EOL "\tDeviceSubClass: %d" EOL "\tDeviceProtocol: %d" EOL "\tMaxPacketSize0: %d" EOL "\tidVendor: %04x" EOL "\tidProduct: %04x" EOL "\tDeviceRelease: %x" EOL "\tNumConfigurations: %d", - (int)count, (int)(desc.bLength), destype(desc.bDescriptorType), + (int)count, (int)bus_number, (int)device_address, + (int)(desc.bLength), destype(desc.bDescriptorType), desc.bcdUSB, (int)(desc.bDeviceClass), (int)(desc.bDeviceSubClass), (int)(desc.bDeviceProtocol), (int)(desc.bMaxPacketSize0), desc.idVendor, desc.idProduct, desc.bcdDevice, (int)(desc.bNumConfigurations)); + } append(buf, tmp, off, len); err = libusb_open(dev, &handle); @@ -432,6 +463,21 @@ static void usb_full(ssize_t count, libusb_device *dev, char **buf, size_t *off, return; } + err = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, man, STRBUFLEN); + if (err < 0) + sprintf((char *)man, "** err(%d)", err); + + err = libusb_get_string_descriptor_ascii(handle, desc.iProduct, prod, STRBUFLEN); + if (err < 0) + sprintf((char *)prod, "** err(%d)", err); + + if (opt_usbdump == 0) { + libusb_close(handle); + sprintf(tmp, EOL " Manufacturer: '%s'" EOL " Product: '%s'", man, prod); + append(buf, tmp, off, len); + return; + } + if (libusb_kernel_driver_active(handle, 0) == 1) { sprintf(tmp, EOL " * dev %d: kernel attached", (int)count); append(buf, tmp, off, len); @@ -497,14 +543,6 @@ static void usb_full(ssize_t count, libusb_device *dev, char **buf, size_t *off, libusb_free_config_descriptor(config); config = NULL; - err = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, man, STRBUFLEN); - if (err < 0) - sprintf((char *)man, "** err(%d)", err); - - err = libusb_get_string_descriptor_ascii(handle, desc.iProduct, prod, STRBUFLEN); - if (err < 0) - sprintf((char *)prod, "** err(%d)", err); - err = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, ser, STRBUFLEN); if (err < 0) sprintf((char *)ser, "** err(%d)", err); @@ -894,6 +932,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 +944,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 +1165,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 +1182,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 +1228,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 +1264,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 +1288,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 +1307,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 +1342,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 +1412,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 +1687,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); + } + } +} diff --git a/usbutils.h b/usbutils.h index fc6340f0..8a364c9b 100644 --- a/usbutils.h +++ b/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)