Browse Source

Merge pull request #336 from kanoi/usb2

mmq usb v0.4 + api usb stats
Con Kolivas 13 years ago
parent
commit
28f251e49e
10 changed files with 2019 additions and 334 deletions
  1. 14 1
      API-README
  2. 60 2
      FPGA-README
  3. 2 2
      Makefile.am
  4. 53 22
      api.c
  5. 20 1
      cgminer.c
  6. 3 3
      configure.ac
  7. 529 299
      driver-modminer.c
  8. 26 4
      miner.h
  9. 1192 0
      usbutils.c
  10. 120 0
      usbutils.h

+ 14 - 1
API-README

@@ -330,6 +330,9 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               queue, scantime, expiry
                               N is an integer in the range 0 to 9999
 
+ substats      USBSTATS       Stats of all LIBUSB mining devices except ztex
+                              e.g. Name=MMQ,ID=0,Stat=SendWork,Count=99,...|
+
 When you enable, disable or restart a GPU or PGA, you will also get Thread messages
 in the cgminer status window
 
@@ -383,7 +386,17 @@ miner.php - an example web page to access the API
 Feature Changelog for external applications using the API:
 
 
-API V1.20
+API V1.21
+
+Added API commands:
+ 'usbstats'
+
+Modified API commands:
+ each MMQ shows up as 4 devices each with it's own stats
+
+----------
+
+API V1.20 (cgminer v2.8.5)
 
 Modified API commands:
  'pools' - add 'Has Stratum', 'Stratum Active', 'Stratum URL'

+ 60 - 2
FPGA-README

@@ -6,7 +6,65 @@ ModMinerQuad (MMQ)
 ------------------
 
 The mining bitstream does not survive a power cycle, so cgminer will upload
-it, if it needs to, before it starts mining
+it, if it needs to, before it starts mining (approx 7min 40sec)
+
+The red LED also flashes while it is uploading the bitstream
+
+-
+
+When mining on windows, the driver being used will determine if mining will work.
+
+If the driver doesn't allow mining, you will get a "USB init," error message
+i.e. one of:
+ open device failed, err %d, you need to install a Windows USB driver for the device
+or
+ kernel detach failed :(
+or
+ claim interface %d failed, err %d
+
+The best solution for this is to use a tool called Zadig to set the driver:
+ http://sourceforge.net/projects/libwdi/files/zadig/
+
+This allows you set the driver for the device to be WinUSB which is usually
+required to make it work if your having problems
+
+You must also make sure you are using the latest libusb-1.0.dll supplied
+with cgminer (not the libusbx version)
+
+-
+
+There is a hidden option in cgminer to dump out a lot of information
+about USB that will help the developers to assist you if you are having
+problems:
+
+ --usb-dump 0
+
+It will only help if you have a working MMQ device attached to the computer
+
+-
+
+If the MMQ doesn't respond to cgminer at all, or the red LED isn't flashing
+then you will need to reset the MMQ
+
+The red LED should always be flashing when it is mining or ready to mine
+
+To reset the MMQ, you are best to press the left "RESET" button on the
+backplane, then unplug and replug the USB cable
+
+If your MMQ doesn't have a button on the "RESET" pad, you need to join
+the two left pads of the "RESET" pad with conductive wire to reset it.
+Cutting a small (metal) paper-clip in half works well for this
+
+Then unplug the USB cable, wait for 5 seconds, then plug it back in
+
+After you press reset, the red LED near the USB port should blink continuously
+
+If it still wont work, power off, wait for 5 seconds, then power on the MMQ
+This of course means it will upload the bitstream again when you start cgminer
+
+-
+
+Device 0 is on the power end of the board
 
 -
 
@@ -64,7 +122,7 @@ a (hack) solution to this is to blacklist the MMQ USB device in
 
 Adding 2 lines like this (just above APC) should help
 # MMQ
-ATTRS{idVendor}=="ifc9", ATTRS{idProduct}=="0003", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0003", ENV{ID_MM_DEVICE_IGNORE}="1"
 
 The change will be lost and need to be re-done, next time you update the
 modem-manager software

+ 2 - 2
Makefile.am

@@ -38,7 +38,7 @@ cgminer_SOURCES := cgminer.c
 
 cgminer_SOURCES	+= elist.h miner.h compat.h bench_block.h	\
 		   util.c util.h uthash.h logging.h		\
-		   sha2.c sha2.h api.c
+		   sha2.c sha2.h api.c usbutils.h
 
 cgminer_SOURCES	+= logging.c
 
@@ -91,7 +91,7 @@ cgminer_SOURCES += driver-icarus.c
 endif
 
 if HAS_MODMINER
-cgminer_SOURCES += driver-modminer.c
+cgminer_SOURCES += driver-modminer.c usbutils.c
 bitstreamsdir = $(bindir)/bitstreams
 dist_bitstreams_DATA = bitstreams/*
 endif

+ 53 - 22
api.c

@@ -131,7 +131,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 
-static const char *APIVERSION = "1.20";
+static const char *APIVERSION = "1.21";
 static const char *DEAD = "Dead";
 #if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
 static const char *SICK = "Sick";
@@ -227,6 +227,7 @@ static const char *OSINFO =
 #define _MINECOIN	"COIN"
 #define _DEBUGSET	"DEBUG"
 #define _SETCONFIG	"SETCONFIG"
+#define _USBSTATS	"USBSTATS"
 
 static const char ISJSON = '{';
 #define JSON0		"{"
@@ -266,6 +267,7 @@ static const char ISJSON = '{';
 #define JSON_MINECOIN	JSON1 _MINECOIN JSON2
 #define JSON_DEBUGSET	JSON1 _DEBUGSET JSON2
 #define JSON_SETCONFIG	JSON1 _SETCONFIG JSON2
+#define JSON_USBSTATS	JSON1 _USBSTATS JSON2
 #define JSON_END	JSON4 JSON5
 
 static const char *JSON_COMMAND = "command";
@@ -369,6 +371,8 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_INVNUM 84
 #define MSG_CONPAR 85
 #define MSG_CONVAL 86
+#define MSG_USBSTA 87
+#define MSG_NOUSTA 88
 
 enum code_severity {
 	SEVERITY_ERR,
@@ -533,6 +537,8 @@ struct CODES {
  { SEVERITY_ERR,   MSG_INVNUM,	PARAM_BOTH,	"Invalid number (%d) for '%s' range is 0-9999" },
  { SEVERITY_ERR,   MSG_CONPAR,	PARAM_NONE,	"Missing config parameters 'name,N'" },
  { SEVERITY_ERR,   MSG_CONVAL,	PARAM_STR,	"Missing config value N for '%s,N'" },
+ { SEVERITY_SUCC,  MSG_USBSTA,	PARAM_NONE,	"USB Statistics" },
+ { SEVERITY_INFO,  MSG_NOUSTA,	PARAM_NONE,	"No USB Statistics" },
  { SEVERITY_FAIL, 0, 0, NULL }
 };
 
@@ -1383,27 +1389,8 @@ static void pgastatus(int pga, bool isjson)
 			frequency = cgpu->device_ztex->freqM1 * (cgpu->device_ztex->freqM + 1);
 #endif
 #ifdef USE_MODMINER
-// TODO: a modminer has up to 4 devices but only 1 set of data for all ...
-// except 4 sets of data for temp/clock
-// So this should change in the future to just find the single temp/clock
-// if the modminer code splits the device into seperate devices later
-// For now, just display the highest temp and the average clock
-		if (cgpu->api == &modminer_api) {
-			int tc = cgpu->threads;
-			int i;
-
-			temp = 0;
-			if (tc > 4)
-				tc = 4;
-			for (i = 0; i < tc; i++) {
-				struct thr_info *thr = cgpu->thr[i];
-				struct modminer_fpga_state *state = thr->cgpu_data;
-				if (state->temp > temp)
-					temp = state->temp;
-				frequency += state->clock;
-			}
-			frequency /= (tc ? tc : 1);
-		}
+		if (cgpu->api == &modminer_api)
+			frequency = cgpu->clock;
 #endif
 
 		cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60;
@@ -2990,6 +2977,49 @@ static void setconfig(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __m
 	strcpy(io_buffer, message(MSG_SETCONFIG, value, param, isjson));
 }
 
+static void usbstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+
+#ifdef USE_MODMINER
+	char buf[TMPBUFSIZ];
+	int count = 0;
+
+	root = api_usb_stats(&count);
+#endif
+
+	if (!root) {
+		strcpy(io_buffer, message(MSG_NOUSTA, 0, NULL, isjson));
+		return;
+	}
+
+#ifdef USE_MODMINER
+
+	strcpy(io_buffer, message(MSG_USBSTA, 0, NULL, isjson));
+
+	if (isjson) {
+		strcat(io_buffer, COMMA);
+		strcat(io_buffer, JSON_USBSTATS);
+	}
+
+	root = print_data(root, buf, isjson);
+	strcat(io_buffer, buf);
+
+	while (42) {
+		root = api_usb_stats(&count);
+		if (!root)
+			break;
+
+		strcat(io_buffer, COMMA);
+		root = print_data(root, buf, isjson);
+		strcat(io_buffer, buf);
+	}
+
+	if (isjson)
+		strcat(io_buffer, JSON_CLOSE);
+#endif
+}
+
 static void checkcommand(__maybe_unused SOCKETTYPE c, char *param, bool isjson, char group);
 
 struct CMDS {
@@ -3045,6 +3075,7 @@ struct CMDS {
 	{ "coin",		minecoin,	false },
 	{ "debug",		debugstate,	true },
 	{ "setconfig",		setconfig,	true },
+	{ "usbstats",		usbstats,	false },
 	{ NULL,			NULL,		false }
 };
 

+ 20 - 1
cgminer.c

@@ -146,6 +146,9 @@ bool opt_disable_pool = true;
 char *opt_icarus_options = NULL;
 char *opt_icarus_timing = NULL;
 bool opt_worktime;
+#ifdef HAVE_LIBUSB
+int opt_usbdump = -1;
+#endif
 
 char *opt_kernel_path;
 char *cgminer_path;
@@ -167,6 +170,10 @@ int gpur_thr_id;
 static int api_thr_id;
 static int total_threads;
 
+#ifdef HAVE_LIBUSB
+pthread_mutex_t cgusb_lock;
+#endif
+
 static pthread_mutex_t hash_lock;
 static pthread_mutex_t qd_lock;
 static pthread_mutex_t *stgd_lock;
@@ -1099,6 +1106,11 @@ 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
+	OPT_WITH_ARG("--usb-dump",
+		     set_int_0_to_10, opt_show_intval, &opt_usbdump,
+		     opt_hidden),
+#endif
 #ifdef HAVE_OPENCL
 	OPT_WITH_ARG("--vectors|-v",
 		     set_vector, NULL, NULL,
@@ -6583,8 +6595,15 @@ int main(int argc, char *argv[])
 	for  (i = 0; i < argc; i++)
 		initial_args[i] = strdup(argv[i]);
 	initial_args[argc] = NULL;
+
 #ifdef HAVE_LIBUSB
-        libusb_init(NULL);
+	int err = libusb_init(NULL);
+	if (err) {
+		fprintf(stderr, "libusb_init() failed err %d", err);
+		fflush(stderr);
+		quit(1, "libusb_init() failed");
+	}
+	mutex_init(&cgusb_lock);
 #endif
 
 	mutex_init(&hash_lock);

+ 3 - 3
configure.ac

@@ -321,7 +321,7 @@ fi
 
 AM_CONDITIONAL([HAS_YASM], [test x$has_yasm = xtrue])
 
-if test "x$bitforce$modminer" != xnono; then
+if test "x$bitforce" != xno; then
 	AC_ARG_WITH([libudev], [AC_HELP_STRING([--without-libudev], [Autodetect FPGAs using libudev (default enabled)])],
 		[libudev=$withval],
 		[libudev=auto]
@@ -343,7 +343,7 @@ AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno])
 
 PKG_PROG_PKG_CONFIG()
 
-if test "x$ztex" != xno; then
+if test "x$ztex$modminer" != xnono; then
   case $target in
     *-*-freebsd*)
       LIBUSB_LIBS="-lusb"
@@ -502,7 +502,7 @@ else
 	echo "  Ztex.FPGAs...........: Disabled"
 fi
 
-if test "x$bitforce$modminer" != xnono; then
+if test "x$bitforce" != xno; then
 	echo "  libudev.detection....: $libudev"
 fi
 

File diff suppressed because it is too large
+ 529 - 299
driver-modminer.c


+ 26 - 4
miner.h

@@ -110,6 +110,10 @@ static inline int fsync (int fd)
   #include "libztex.h"
 #endif
 
+#ifdef USE_MODMINER
+  #include "usbutils.h"
+#endif
+
 #if !defined(WIN32) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
 #define bswap_16 __builtin_bswap16
 #define bswap_32 __builtin_bswap32
@@ -360,9 +364,20 @@ struct cgpu_info {
 	union {
 #ifdef USE_ZTEX
 		struct libztex_device *device_ztex;
+#endif
+#ifdef USE_MODMINER
+		struct cg_usb_device *usbdev;
 #endif
 		int device_fd;
 	};
+#ifdef USE_MODMINER
+	int usbstat;
+	char fpgaid;
+	unsigned char clock;
+	pthread_mutex_t *modminer_mutex;
+	bool tried_two_byte_temp;
+	bool one_byte_temp;
+#endif
 #ifdef USE_BITFORCE
 	struct timeval work_start_tv;
 	unsigned int wait_ms;
@@ -373,9 +388,8 @@ struct cgpu_info {
 	bool nonce_range;
 	bool polling;
 	bool flash_led;
+	pthread_mutex_t device_mutex;
 #endif
-	pthread_mutex_t		device_mutex;
-
 	enum dev_enable deven;
 	int accepted;
 	int rejected;
@@ -654,6 +668,9 @@ extern bool opt_restart;
 extern char *opt_icarus_options;
 extern char *opt_icarus_timing;
 extern bool opt_worktime;
+#ifdef HAVE_LIBUSB
+extern int opt_usbdump;
+#endif
 #ifdef USE_BITFORCE
 extern bool opt_bfl_noncerange;
 #endif
@@ -684,6 +701,10 @@ extern int opt_queue;
 extern int opt_scantime;
 extern int opt_expiry;
 
+#ifdef HAVE_LIBUSB
+extern pthread_mutex_t cgusb_lock;
+#endif
+
 extern pthread_mutex_t console_lock;
 extern pthread_mutex_t ch_lock;
 
@@ -998,9 +1019,10 @@ struct modminer_fpga_state {
 	uint32_t hashes;
 
 	char next_work_cmd[46];
+	char fpgaid;
 
-	unsigned char clock;
-	float temp;
+	bool overheated;
+	bool new_work;
 
 	uint32_t shares;
 	uint32_t shares_last_hw;

+ 1192 - 0
usbutils.c

@@ -0,0 +1,1192 @@
+/*
+ * Copyright 2012 Andrew Smith
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "logging.h"
+#include "miner.h"
+#include "usbutils.h"
+
+#ifdef USE_ICARUS
+#define DRV_ICARUS 1
+#endif
+
+#ifdef USE_BITFORCE
+#define DRV_BITFORCE 2
+#endif
+
+#ifdef USE_MODMINER
+#define DRV_MODMINER 3
+#endif
+
+#define DRV_LAST -1
+
+#define USB_CONFIG 1
+
+#define EPI(x) (LIBUSB_ENDPOINT_IN | (unsigned char)(x))
+#define EPO(x) (LIBUSB_ENDPOINT_OUT | (unsigned char)(x))
+
+#ifdef WIN32
+#define MODMINER_TIMEOUT_MS 200
+#else
+#define MODMINER_TIMEOUT_MS 100
+#endif
+
+#ifdef USE_MODMINER
+static struct usb_endpoints mmq_eps[] = {
+	{ LIBUSB_TRANSFER_TYPE_BULK,	64,	EPI(3), 0 },
+	{ LIBUSB_TRANSFER_TYPE_BULK,	64,	EPO(3), 0 }
+};
+#endif
+
+// TODO: Add support for (at least) Interrupt endpoints
+static struct usb_find_devices find_dev[] = {
+/*
+#ifdef USE_ICARUS
+	{ DRV_ICARUS, 	"ICA",	0x067b,	0x0230,	true,	EPI(3),	EPO(2), 1 },
+	{ DRV_ICARUS, 	"LOT",	0x0403,	0x6001,	false,	EPI(0),	EPO(0), 1 },
+	{ DRV_ICARUS, 	"CM1",	0x067b,	0x0230,	false,	EPI(0),	EPO(0), 1 },
+#endif
+#ifdef USE_BITFORCE
+	{ DRV_BITFORCE,	"BFL",	0x0403,	0x6014,	true,	EPI(1),	EPO(2), 1 },
+#endif
+*/
+#ifdef USE_MODMINER
+	{
+		.drv = DRV_MODMINER,
+		.name = "MMQ",
+		.idVendor = 0x1fc9,
+		.idProduct = 0x0003,
+		.config = 1,
+		.interface = 1,
+		.timeout = MODMINER_TIMEOUT_MS,
+		.epcount = ARRAY_SIZE(mmq_eps),
+		.eps = mmq_eps },
+#endif
+	{ DRV_LAST, NULL, 0, 0, 0, 0, 0, 0, NULL }
+};
+
+#ifdef USE_BITFORCE
+extern struct device_api bitforce_api;
+#endif
+
+#ifdef USE_ICARUS
+extern struct device_api icarus_api;
+#endif
+
+#ifdef USE_MODMINER
+extern struct device_api modminer_api;
+#endif
+
+/*
+ * Our own internal list of used USB devices
+ * So two drivers or a single driver searching
+ * can't touch the same device during detection
+ */
+struct usb_list {
+	uint8_t bus_number;
+	uint8_t device_address;
+	uint8_t filler[2];
+	struct usb_list *prev;
+	struct usb_list *next;
+};
+
+#define STRBUFLEN 256
+static const char *BLANK = "";
+
+static pthread_mutex_t *list_lock = NULL;
+static struct usb_list *usb_head = NULL;
+
+struct cg_usb_stats_item {
+	uint64_t count;
+	double total_delay;
+	double min_delay;
+	double max_delay;
+	struct timeval first;
+	struct timeval last;
+};
+
+#define CMD_CMD 0
+#define CMD_TIMEOUT 1
+#define CMD_ERROR 2
+
+struct cg_usb_stats_details {
+	int seq;
+	struct cg_usb_stats_item item[CMD_ERROR+1];
+};
+
+struct cg_usb_stats {
+	char *name;
+	int device_id;
+	struct cg_usb_stats_details *details;
+};
+
+#define SEQ0 0
+#define SEQ1 1
+
+static struct cg_usb_stats *usb_stats = NULL;
+static int next_stat = 0;
+
+static const char **usb_commands;
+
+static const char *C_PING_S = "Ping";
+static const char *C_CLEAR_S = "Clear";
+static const char *C_REQUESTVERSION_S = "RequestVersion";
+static const char *C_GETVERSION_S = "GetVersion";
+static const char *C_REQUESTFPGACOUNT_S = "RequestFPGACount";
+static const char *C_GETFPGACOUNT_S = "GetFPGACount";
+static const char *C_STARTPROGRAM_S = "StartProgram";
+static const char *C_STARTPROGRAMSTATUS_S = "StartProgramStatus";
+static const char *C_PROGRAM_S = "Program";
+static const char *C_PROGRAMSTATUS_S = "ProgramStatus";
+static const char *C_PROGRAMSTATUS2_S = "ProgramStatus2";
+static const char *C_FINALPROGRAMSTATUS_S = "FinalProgramStatus";
+static const char *C_SETCLOCK_S = "SetClock";
+static const char *C_REPLYSETCLOCK_S = "ReplySetClock";
+static const char *C_REQUESTUSERCODE_S = "RequestUserCode";
+static const char *C_GETUSERCODE_S = "GetUserCode";
+static const char *C_REQUESTTEMPERATURE_S = "RequestTemperature";
+static const char *C_GETTEMPERATURE_S = "GetTemperature";
+static const char *C_SENDWORK_S = "SendWork";
+static const char *C_SENDWORKSTATUS_S = "SendWorkStatus";
+static const char *C_REQUESTWORKSTATUS_S = "RequestWorkStatus";
+static const char *C_GETWORKSTATUS_S = "GetWorkStatus";
+
+#ifdef EOL
+#undef EOL
+#endif
+#define EOL "\n"
+
+static const char *DESDEV = "Device";
+static const char *DESCON = "Config";
+static const char *DESSTR = "String";
+static const char *DESINT = "Interface";
+static const char *DESEP = "Endpoint";
+static const char *DESHID = "HID";
+static const char *DESRPT = "Report";
+static const char *DESPHY = "Physical";
+static const char *DESHUB = "Hub";
+
+static const char *EPIN = "In: ";
+static const char *EPOUT = "Out: ";
+static const char *EPX = "?: ";
+
+static const char *CONTROL = "Control";
+static const char *ISOCHRONOUS_X = "Isochronous+?";
+static const char *ISOCHRONOUS_N_X = "Isochronous+None+?";
+static const char *ISOCHRONOUS_N_D = "Isochronous+None+Data";
+static const char *ISOCHRONOUS_N_F = "Isochronous+None+Feedback";
+static const char *ISOCHRONOUS_N_I = "Isochronous+None+Implicit";
+static const char *ISOCHRONOUS_A_X = "Isochronous+Async+?";
+static const char *ISOCHRONOUS_A_D = "Isochronous+Async+Data";
+static const char *ISOCHRONOUS_A_F = "Isochronous+Async+Feedback";
+static const char *ISOCHRONOUS_A_I = "Isochronous+Async+Implicit";
+static const char *ISOCHRONOUS_D_X = "Isochronous+Adaptive+?";
+static const char *ISOCHRONOUS_D_D = "Isochronous+Adaptive+Data";
+static const char *ISOCHRONOUS_D_F = "Isochronous+Adaptive+Feedback";
+static const char *ISOCHRONOUS_D_I = "Isochronous+Adaptive+Implicit";
+static const char *ISOCHRONOUS_S_X = "Isochronous+Sync+?";
+static const char *ISOCHRONOUS_S_D = "Isochronous+Sync+Data";
+static const char *ISOCHRONOUS_S_F = "Isochronous+Sync+Feedback";
+static const char *ISOCHRONOUS_S_I = "Isochronous+Sync+Implicit";
+static const char *BULK = "Bulk";
+static const char *INTERRUPT = "Interrupt";
+static const char *UNKNOWN = "Unknown";
+
+static const char *destype(uint8_t bDescriptorType)
+{
+	switch (bDescriptorType) {
+		case LIBUSB_DT_DEVICE:
+			return DESDEV;
+		case LIBUSB_DT_CONFIG:
+			return DESCON;
+		case LIBUSB_DT_STRING:
+			return DESSTR;
+		case LIBUSB_DT_INTERFACE:
+			return DESINT;
+		case LIBUSB_DT_ENDPOINT:
+			return DESEP;
+		case LIBUSB_DT_HID:
+			return DESHID;
+		case LIBUSB_DT_REPORT:
+			return DESRPT;
+		case LIBUSB_DT_PHYSICAL:
+			return DESPHY;
+		case LIBUSB_DT_HUB:
+			return DESHUB;
+	}
+	return UNKNOWN;
+}
+
+static const char *epdir(uint8_t bEndpointAddress)
+{
+	switch (bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) {
+		case LIBUSB_ENDPOINT_IN:
+			return EPIN;
+		case LIBUSB_ENDPOINT_OUT:
+			return EPOUT;
+	}
+	return EPX;
+}
+
+static const char *epatt(uint8_t bmAttributes)
+{
+	switch(bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) {
+		case LIBUSB_TRANSFER_TYPE_CONTROL:
+			return CONTROL;
+		case LIBUSB_TRANSFER_TYPE_BULK:
+			return BULK;
+		case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+			return INTERRUPT;
+		case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+			switch(bmAttributes & LIBUSB_ISO_SYNC_TYPE_MASK) {
+				case LIBUSB_ISO_SYNC_TYPE_NONE:
+					switch(bmAttributes & LIBUSB_ISO_USAGE_TYPE_MASK) {
+						case LIBUSB_ISO_USAGE_TYPE_DATA:
+							return ISOCHRONOUS_N_D;
+						case LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
+							return ISOCHRONOUS_N_F;
+						case LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
+							return ISOCHRONOUS_N_I;
+					}
+					return ISOCHRONOUS_N_X;
+				case LIBUSB_ISO_SYNC_TYPE_ASYNC:
+					switch(bmAttributes & LIBUSB_ISO_USAGE_TYPE_MASK) {
+						case LIBUSB_ISO_USAGE_TYPE_DATA:
+							return ISOCHRONOUS_A_D;
+						case LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
+							return ISOCHRONOUS_A_F;
+						case LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
+							return ISOCHRONOUS_A_I;
+					}
+					return ISOCHRONOUS_A_X;
+				case LIBUSB_ISO_SYNC_TYPE_ADAPTIVE:
+					switch(bmAttributes & LIBUSB_ISO_USAGE_TYPE_MASK) {
+						case LIBUSB_ISO_USAGE_TYPE_DATA:
+							return ISOCHRONOUS_D_D;
+						case LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
+							return ISOCHRONOUS_D_F;
+						case LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
+							return ISOCHRONOUS_D_I;
+					}
+					return ISOCHRONOUS_D_X;
+				case LIBUSB_ISO_SYNC_TYPE_SYNC:
+					switch(bmAttributes & LIBUSB_ISO_USAGE_TYPE_MASK) {
+						case LIBUSB_ISO_USAGE_TYPE_DATA:
+							return ISOCHRONOUS_S_D;
+						case LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
+							return ISOCHRONOUS_S_F;
+						case LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
+							return ISOCHRONOUS_S_I;
+					}
+					return ISOCHRONOUS_S_X;
+			}
+			return ISOCHRONOUS_X;
+	}
+
+	return UNKNOWN;
+}
+
+static void append(char **buf, char *append, size_t *off, size_t *len)
+{
+	int new = strlen(append);
+	if ((new + *off) >= *len)
+	{
+		*len *= 2;
+		*buf = realloc(*buf, *len);
+	}
+
+	strcpy(*buf + *off, append);
+	*off += new;
+}
+
+static bool setgetdes(ssize_t count, libusb_device *dev, struct libusb_device_handle *handle, struct libusb_config_descriptor **config, int cd, char **buf, size_t *off, size_t *len)
+{
+	char tmp[512];
+	int err;
+
+	err = libusb_set_configuration(handle, cd);
+	if (err) {
+		sprintf(tmp, EOL "  ** dev %d: Failed to set config descriptor to %d, err %d",
+				(int)count, cd, err);
+		append(buf, tmp, off, len);
+		return false;
+	}
+
+	err = libusb_get_active_config_descriptor(dev, config);
+	if (err) {
+		sprintf(tmp, EOL "  ** dev %d: Failed to get active config descriptor set to %d, err %d",
+				(int)count, cd, err);
+		append(buf, tmp, off, len);
+		return false;
+	}
+
+	sprintf(tmp, EOL "  ** dev %d: Set & Got active config descriptor to %d, err %d",
+			(int)count, cd, err);
+	append(buf, tmp, off, len);
+	return true;
+}
+
+static void usb_full(ssize_t count, libusb_device *dev, char **buf, size_t *off, size_t *len)
+{
+	struct libusb_device_descriptor desc;
+	struct libusb_device_handle *handle;
+	struct libusb_config_descriptor *config;
+	const struct libusb_interface_descriptor *idesc;
+	const struct libusb_endpoint_descriptor *epdesc;
+	unsigned char man[STRBUFLEN+1];
+	unsigned char prod[STRBUFLEN+1];
+	unsigned char ser[STRBUFLEN+1];
+	char tmp[512];
+	int err, i, j, k;
+
+	err = libusb_get_device_descriptor(dev, &desc);
+	if (err) {
+		sprintf(tmp, EOL ".USB dev %d: Failed to get descriptor, err %d",
+					(int)count, err);
+		append(buf, tmp, off, len);
+		return;
+	}
+
+	sprintf(tmp, EOL ".USB dev %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),
+				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);
+	if (err) {
+		sprintf(tmp, EOL "  ** dev %d: Failed to open, err %d", (int)count, err);
+		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);
+	}
+
+	err = libusb_get_active_config_descriptor(dev, &config);
+	if (err) {
+		if (!setgetdes(count, dev, handle, &config, 1, buf, off, len)
+		&&  !setgetdes(count, dev, handle, &config, 0, buf, off, len)) {
+			libusb_close(handle);
+			sprintf(tmp, EOL "  ** dev %d: Failed to set config descriptor to %d or %d",
+					(int)count, 1, 0);
+			append(buf, tmp, off, len);
+			return;
+		}
+	}
+
+	sprintf(tmp, EOL "     dev %d: Active Config:" EOL "\tDescriptorType: %s" EOL
+			"\tNumInterfaces: %d" EOL "\tConfigurationValue: %d" EOL
+			"\tAttributes: %d" EOL "\tMaxPower: %d",
+				(int)count, destype(config->bDescriptorType),
+				(int)(config->bNumInterfaces), (int)(config->iConfiguration),
+				(int)(config->bmAttributes), (int)(config->MaxPower));
+	append(buf, tmp, off, len);
+
+	for (i = 0; i < (int)(config->bNumInterfaces); i++) {
+		for (j = 0; j < config->interface[i].num_altsetting; j++) {
+			idesc = &(config->interface[i].altsetting[j]);
+
+			sprintf(tmp, EOL "     _dev %d: Interface Descriptor %d:" EOL
+					"\tDescriptorType: %s" EOL "\tInterfaceNumber: %d" EOL
+					"\tNumEndpoints: %d" EOL "\tInterfaceClass: %d" EOL
+					"\tInterfaceSubClass: %d" EOL "\tInterfaceProtocol: %d",
+						(int)count, j, destype(idesc->bDescriptorType),
+						(int)(idesc->bInterfaceNumber),
+						(int)(idesc->bNumEndpoints),
+						(int)(idesc->bInterfaceClass),
+						(int)(idesc->bInterfaceSubClass),
+						(int)(idesc->bInterfaceProtocol));
+			append(buf, tmp, off, len);
+
+			for (k = 0; k < (int)(idesc->bNumEndpoints); k++) {
+				epdesc = &(idesc->endpoint[k]);
+
+				sprintf(tmp, EOL "     __dev %d: Interface %d Endpoint %d:" EOL
+						"\tDescriptorType: %s" EOL
+						"\tEndpointAddress: %s0x%x" EOL
+						"\tAttributes: %s" EOL "\tMaxPacketSize: %d" EOL
+						"\tInterval: %d" EOL "\tRefresh: %d",
+							(int)count, (int)(idesc->bInterfaceNumber), k,
+							destype(epdesc->bDescriptorType),
+							epdir(epdesc->bEndpointAddress),
+							(int)(epdesc->bEndpointAddress),
+							epatt(epdesc->bmAttributes),
+							epdesc->wMaxPacketSize,
+							(int)(epdesc->bInterval),
+							(int)(epdesc->bRefresh));
+				append(buf, tmp, off, len);
+			}
+		}
+	}
+
+	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);
+
+	sprintf(tmp, EOL "     dev %d: More Info:" EOL "\tManufacturer: '%s'" EOL
+			"\tProduct: '%s'" EOL "\tSerial '%s'",
+				(int)count, man, prod, ser);
+	append(buf, tmp, off, len);
+
+	libusb_close(handle);
+}
+
+// Function to dump all USB devices
+static void usb_all()
+{
+	libusb_device **list;
+	ssize_t count, i;
+	char *buf;
+	size_t len, off;
+
+	count = libusb_get_device_list(NULL, &list);
+	if (count < 0) {
+		applog(LOG_ERR, "USB all: failed, err %d", (int)count);
+		return;
+	}
+
+	if (count == 0)
+		applog(LOG_WARNING, "USB all: found no devices");
+	else
+	{
+		len = 10000;
+		buf = malloc(len+1);
+
+		sprintf(buf, "USB all: found %d devices", (int)count);
+
+		off = strlen(buf);
+
+		for (i = 0; i < count; i++)
+			usb_full(i, list[i], &buf, &off, &len);
+
+		applog(LOG_WARNING, "%s", buf);
+
+		free(buf);
+	}
+
+	libusb_free_device_list(list, 1);
+}
+
+static void cgusb_check_init()
+{
+	mutex_lock(&cgusb_lock);
+
+	if (list_lock == NULL) {
+		list_lock = calloc(1, sizeof(*list_lock));
+		mutex_init(list_lock);
+
+		if (opt_usbdump >= 0) {
+			libusb_set_debug(NULL, opt_usbdump);
+			usb_all();
+		}
+
+		usb_commands = malloc(sizeof(*usb_commands) * C_MAX);
+
+		// use constants so the stat generation is very quick
+		// and the association between number and name can't
+		// be missalined easily
+		usb_commands[C_PING] = C_PING_S;
+		usb_commands[C_CLEAR] = C_CLEAR_S;
+		usb_commands[C_REQUESTVERSION] = C_REQUESTVERSION_S;
+		usb_commands[C_GETVERSION] = C_GETVERSION_S;
+		usb_commands[C_REQUESTFPGACOUNT] = C_REQUESTFPGACOUNT_S;
+		usb_commands[C_GETFPGACOUNT] = C_GETFPGACOUNT_S;
+		usb_commands[C_STARTPROGRAM] = C_STARTPROGRAM_S;
+		usb_commands[C_STARTPROGRAMSTATUS] = C_STARTPROGRAMSTATUS_S;
+		usb_commands[C_PROGRAM] = C_PROGRAM_S;
+		usb_commands[C_PROGRAMSTATUS] = C_PROGRAMSTATUS_S;
+		usb_commands[C_PROGRAMSTATUS2] = C_PROGRAMSTATUS2_S;
+		usb_commands[C_FINALPROGRAMSTATUS] = C_FINALPROGRAMSTATUS_S;
+		usb_commands[C_SETCLOCK] = C_SETCLOCK_S;
+		usb_commands[C_REPLYSETCLOCK] = C_REPLYSETCLOCK_S;
+		usb_commands[C_REQUESTUSERCODE] = C_REQUESTUSERCODE_S;
+		usb_commands[C_GETUSERCODE] = C_GETUSERCODE_S;
+		usb_commands[C_REQUESTTEMPERATURE] = C_REQUESTTEMPERATURE_S;
+		usb_commands[C_GETTEMPERATURE] = C_GETTEMPERATURE_S;
+		usb_commands[C_SENDWORK] = C_SENDWORK_S;
+		usb_commands[C_SENDWORKSTATUS] = C_SENDWORKSTATUS_S;
+		usb_commands[C_REQUESTWORKSTATUS] = C_REQUESTWORKSTATUS_S;
+		usb_commands[C_GETWORKSTATUS] = C_GETWORKSTATUS_S;
+	}
+
+	mutex_unlock(&cgusb_lock);
+}
+
+static bool in_use(libusb_device *dev, bool lock)
+{
+	struct usb_list *usb_tmp;
+	bool used = false;
+	uint8_t bus_number;
+	uint8_t device_address;
+
+	bus_number = libusb_get_bus_number(dev);
+	device_address = libusb_get_device_address(dev);
+
+	if (lock)
+		mutex_lock(list_lock);
+
+	if ((usb_tmp = usb_head))
+		do {
+			if (bus_number == usb_tmp->bus_number
+			&&  device_address == usb_tmp->device_address) {
+				used = true;
+				break;
+			}
+
+			usb_tmp = usb_tmp->next;
+
+		} while (usb_tmp != usb_head);
+
+	if (lock)
+		mutex_unlock(list_lock);
+
+	return used;
+}
+
+static void add_used(libusb_device *dev, bool lock)
+{
+	struct usb_list *usb_tmp;
+	char buf[128];
+	uint8_t bus_number;
+	uint8_t device_address;
+
+	bus_number = libusb_get_bus_number(dev);
+	device_address = libusb_get_device_address(dev);
+
+	if (lock)
+		mutex_lock(list_lock);
+
+	if (in_use(dev, false)) {
+		if (lock)
+			mutex_unlock(list_lock);
+
+		sprintf(buf, "add_used() duplicate bus_number %d device_address %d",
+				bus_number, device_address);
+		quit(1, buf);
+	}
+
+	usb_tmp = malloc(sizeof(*usb_tmp));
+
+	usb_tmp->bus_number = bus_number;
+	usb_tmp->device_address = device_address;
+
+	if (usb_head) {
+		// add to end
+		usb_tmp->prev = usb_head->prev;
+		usb_tmp->next = usb_head;
+		usb_head->prev = usb_tmp;
+		usb_tmp->prev->next = usb_tmp;
+	} else {
+		usb_tmp->prev = usb_tmp;
+		usb_tmp->next = usb_tmp;
+		usb_head = usb_tmp;
+	}
+
+	if (lock)
+		mutex_unlock(list_lock);
+}
+
+static void release(uint8_t bus_number, uint8_t device_address, bool lock)
+{
+	struct usb_list *usb_tmp;
+	bool found = false;
+	char buf[128];
+
+	if (lock)
+		mutex_lock(list_lock);
+
+	usb_tmp = usb_head;
+	if (usb_tmp)
+		do {
+			if (bus_number == usb_tmp->bus_number
+			&&  device_address == usb_tmp->device_address) {
+				found = true;
+				break;
+			}
+
+			usb_tmp = usb_tmp->next;
+
+		} while (usb_tmp != usb_head);
+
+	if (!found) {
+		if (lock)
+			mutex_unlock(list_lock);
+
+		sprintf(buf, "release() unknown: bus_number %d device_address %d",
+				bus_number, device_address);
+		quit(1, buf);
+	}
+
+	if (usb_tmp->next == usb_tmp) {
+		usb_head = NULL;
+	} else {
+		usb_tmp->next->prev = usb_tmp->prev;
+		usb_tmp->prev->next = usb_tmp->next;
+	}
+
+	if (lock)
+		mutex_unlock(list_lock);
+
+	free(usb_tmp);
+}
+
+static void release_dev(libusb_device *dev, bool lock)
+{
+	uint8_t bus_number;
+	uint8_t device_address;
+
+	bus_number = libusb_get_bus_number(dev);
+	device_address = libusb_get_device_address(dev);
+
+	release(bus_number, device_address, lock);
+}
+
+#if 0
+static void release_cgusb(struct cg_usb_device *cgusb, bool lock)
+{
+	release(cgusb->bus_number, cgusb->device_address, lock);
+}
+#endif
+
+static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb)
+{
+	if (cgusb->serial_string && cgusb->serial_string != BLANK)
+		free(cgusb->serial_string);
+
+	if (cgusb->manuf_string && cgusb->manuf_string != BLANK)
+		free(cgusb->manuf_string);
+
+	if (cgusb->prod_string && cgusb->prod_string != BLANK)
+		free(cgusb->prod_string);
+
+	free(cgusb->descriptor);
+
+	free(cgusb);
+
+	return NULL;
+}
+
+void usb_uninit(struct cgpu_info *cgpu)
+{
+	libusb_release_interface(cgpu->usbdev->handle, cgpu->usbdev->found->interface);
+	libusb_close(cgpu->usbdev->handle);
+	cgpu->usbdev = free_cgusb(cgpu->usbdev);
+}
+
+bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found)
+{
+	struct cg_usb_device *cgusb = NULL;
+	struct libusb_config_descriptor *config = NULL;
+	const struct libusb_interface_descriptor *idesc;
+	const struct libusb_endpoint_descriptor *epdesc;
+	unsigned char strbuf[STRBUFLEN+1];
+	int err, i, j, k;
+
+	cgusb = calloc(1, sizeof(*cgusb));
+	cgusb->found = found;
+	cgusb->descriptor = calloc(1, sizeof(*(cgusb->descriptor)));
+
+	err = libusb_get_device_descriptor(dev, cgusb->descriptor);
+	if (err) {
+		applog(LOG_ERR, "USB init failed to get descriptor, err %d", err);
+		goto dame;
+	}
+
+	err = libusb_open(dev, &(cgusb->handle));
+	if (err) {
+		switch (err) {
+			case LIBUSB_ERROR_ACCESS:
+				applog(LOG_ERR, "USB init open device failed, err %d, you dont have priviledge to access the device", err);
+				break;
+#ifdef WIN32
+			// Windows specific message
+			case LIBUSB_ERROR_NOT_SUPPORTED:
+				applog(LOG_ERR, "USB init, open device failed, err %d, you need to install a Windows USB driver for the device", err);
+				break;
+#endif
+			default:
+				applog(LOG_ERR, "USB init, open device failed, err %d", err);
+		}
+
+		goto dame;
+	}
+
+	if (libusb_kernel_driver_active(cgusb->handle, 0) == 1) {
+		applog(LOG_WARNING, "USB init, kernel attached ...");
+		if (libusb_detach_kernel_driver(cgusb->handle, 0) == 0)
+			applog(LOG_WARNING, "USB init, kernel detached successfully");
+		else
+			applog(LOG_WARNING, "USB init, kernel detach failed :(");
+	}
+
+	err = libusb_set_configuration(cgusb->handle, found->config);
+	if (err) {
+		applog(LOG_DEBUG, "USB init, failed to set config to %d, err %d",
+			found->config, err);
+		goto cldame;
+	}
+
+	err = libusb_get_active_config_descriptor(dev, &config);
+	if (err) {
+		applog(LOG_DEBUG, "USB init, failed to get config descriptor %d, err %d",
+			found->config, err);
+		goto cldame;
+	}
+
+	if ((int)(config->bNumInterfaces) < found->interface)
+		goto cldame;
+
+	for (i = 0; i < found->epcount; i++)
+		found->eps[i].found = false;
+
+	for (i = 0; i < config->interface[found->interface].num_altsetting; i++) {
+		idesc = &(config->interface[found->interface].altsetting[i]);
+		for (j = 0; j < (int)(idesc->bNumEndpoints); j++) {
+			epdesc = &(idesc->endpoint[j]);
+			for (k = 0; k < found->epcount; k++) {
+				if (!found->eps[k].found) {
+					if (epdesc->bmAttributes == found->eps[k].att
+					&&  epdesc->wMaxPacketSize >= found->eps[k].size
+					&&  epdesc->bEndpointAddress == found->eps[k].ep) {
+						found->eps[k].found = true;
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	for (i = 0; i < found->epcount; i++)
+		if (found->eps[i].found == false)
+			goto cldame;
+
+	err = libusb_claim_interface(cgusb->handle, found->interface);
+	if (err) {
+		applog(LOG_DEBUG, "USB init, claim interface %d failed, err %d",
+			found->interface, err);
+		goto cldame;
+	}
+
+	cgusb->bus_number = libusb_get_bus_number(dev);
+	cgusb->device_address = libusb_get_device_address(dev);
+	cgusb->usbver = cgusb->descriptor->bcdUSB;
+
+// TODO: allow this with the right version of the libusb include and running library
+//	cgusb->speed = libusb_get_device_speed(dev);
+
+	err = libusb_get_string_descriptor_ascii(cgusb->handle,
+				cgusb->descriptor->iProduct, strbuf, STRBUFLEN);
+	if (err > 0)
+		cgusb->prod_string = strdup((char *)strbuf);
+	else
+		cgusb->prod_string = (char *)BLANK;
+
+	err = libusb_get_string_descriptor_ascii(cgusb->handle,
+				cgusb->descriptor->iManufacturer, strbuf, STRBUFLEN);
+	if (err > 0)
+		cgusb->manuf_string = strdup((char *)strbuf);
+	else
+		cgusb->manuf_string = (char *)BLANK;
+
+	err = libusb_get_string_descriptor_ascii(cgusb->handle,
+				cgusb->descriptor->iSerialNumber, strbuf, STRBUFLEN);
+	if (err > 0)
+		cgusb->serial_string = strdup((char *)strbuf);
+	else
+		cgusb->serial_string = (char *)BLANK;
+
+// TODO: ?
+//	cgusb->fwVersion <- for temp1/temp2 decision? or serial? (driver-modminer.c)
+//	cgusb->interfaceVersion
+
+	applog(LOG_DEBUG, "USB init device bus_number=%d device_address=%d usbver=%04x prod='%s' manuf='%s' serial='%s'", (int)(cgusb->bus_number), (int)(cgusb->device_address), cgusb->usbver, cgusb->prod_string, cgusb->manuf_string, cgusb->serial_string);
+
+	cgpu->usbdev = cgusb;
+
+	libusb_free_config_descriptor(config);
+
+	return true;
+
+cldame:
+
+	libusb_close(cgusb->handle);
+
+dame:
+
+	if (config)
+		libusb_free_config_descriptor(config);
+
+	cgusb = free_cgusb(cgusb);
+
+	return false;
+}
+
+static bool usb_check_device(struct device_api *api, struct libusb_device *dev, struct usb_find_devices *look)
+{
+	struct libusb_device_descriptor desc;
+	int err;
+
+	err = libusb_get_device_descriptor(dev, &desc);
+	if (err) {
+		applog(LOG_DEBUG, "USB check device: Failed to get descriptor, err %d", err);
+		return false;
+	}
+
+	if (desc.idVendor != look->idVendor || desc.idProduct != look->idProduct) {
+		applog(LOG_DEBUG, "%s looking for %04x:%04x but found %04x:%04x instead",
+			api->name, look->idVendor, look->idProduct, desc.idVendor, desc.idProduct);
+
+		return false;
+	}
+
+	applog(LOG_DEBUG, "%s looking for and found %04x:%04x",
+		api->name, look->idVendor, look->idProduct);
+
+	return true;
+}
+
+static struct usb_find_devices *usb_check_each(int drv, struct device_api *api, struct libusb_device *dev)
+{
+	struct usb_find_devices *found;
+	int i;
+
+	for (i = 0; find_dev[i].drv != DRV_LAST; i++)
+		if (find_dev[i].drv == drv) {
+			if (usb_check_device(api, dev, &(find_dev[i]))) {
+				found = malloc(sizeof(*found));
+				memcpy(found, &(find_dev[i]), sizeof(*found));
+				return found;
+			}
+		}
+
+	return NULL;
+}
+
+static struct usb_find_devices *usb_check(__maybe_unused struct device_api *api, __maybe_unused struct libusb_device *dev)
+{
+#ifdef USE_BITFORCE
+	if (api == &bitforce_api)
+		return usb_check_each(DRV_BITFORCE, api, dev);
+#endif
+
+#ifdef USE_ICARUS
+	if (api == &icarus_api)
+		return usb_check_each(DRV_ICARUS, api, dev);
+#endif
+
+#ifdef USE_MODMINER
+	if (api == &modminer_api)
+		return usb_check_each(DRV_MODMINER, api, dev);
+#endif
+
+	return NULL;
+}
+
+void usb_detect(struct device_api *api, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *))
+{
+	libusb_device **list;
+	ssize_t count, i;
+	struct usb_find_devices *found;
+
+	cgusb_check_init();
+
+	count = libusb_get_device_list(NULL, &list);
+	if (count < 0) {
+		applog(LOG_DEBUG, "USB scan devices: failed, err %d", count);
+		return;
+	}
+
+	if (count == 0)
+		applog(LOG_DEBUG, "USB scan devices: found no devices");
+
+	for (i = 0; i < count; i++) {
+		mutex_lock(list_lock);
+
+		if (in_use(list[i], false))
+			mutex_unlock(list_lock);
+		else {
+			add_used(list[i], false);
+
+			mutex_unlock(list_lock);
+
+			found = usb_check(api, list[i]);
+			if (!found)
+				release_dev(list[i], true);
+			else
+				if (!device_detect(list[i], found))
+					release_dev(list[i], true);
+		}
+	}
+
+	libusb_free_device_list(list, 1);
+}
+
+// Set this to 0 to remove stats processing
+#define DO_USB_STATS 1
+
+#if DO_USB_STATS
+#define USB_STATS(sgpu, sta, fin, err, cmd, seq) stats(cgpu, sta, fin, err, cmd, seq)
+#define STATS_TIMEVAL(tv) gettimeofday(tv, NULL)
+#else
+#define USB_STATS(sgpu, sta, fin, err, cmd, seq)
+#define STATS_TIMEVAL(tv)
+#endif
+
+// The stat data can be spurious due to not locking it before copying it -
+// however that would require the stat() function to also lock and release
+// a mutex every time a usb read or write is called which would slow
+// things down more
+struct api_data *api_usb_stats(__maybe_unused int *count)
+{
+#if DO_USB_STATS
+	struct cg_usb_stats_details *details;
+	struct cg_usb_stats *sta;
+	struct api_data *root = NULL;
+	int device;
+	int cmdseq;
+
+	cgusb_check_init();
+
+	if (next_stat == 0)
+		return NULL;
+
+	while (*count < next_stat * C_MAX * 2) {
+		device = *count / (C_MAX * 2);
+		cmdseq = *count % (C_MAX * 2);
+
+		(*count)++;
+
+		sta = &(usb_stats[device]);
+		details = &(sta->details[cmdseq]);
+
+		// Only show stats that have results
+		if (details->item[CMD_CMD].count == 0 &&
+		    details->item[CMD_TIMEOUT].count == 0 &&
+		    details->item[CMD_ERROR].count == 0)
+			continue;
+
+		root = api_add_string(root, "Name", sta->name, false);
+		root = api_add_int(root, "ID", &(sta->device_id), false);
+		root = api_add_const(root, "Stat", usb_commands[cmdseq/2], false);
+		root = api_add_int(root, "Seq", &(details->seq), true);
+		root = api_add_uint64(root, "Count",
+					&(details->item[CMD_CMD].count), true);
+		root = api_add_double(root, "Total Delay",
+					&(details->item[CMD_CMD].total_delay), true);
+		root = api_add_double(root, "Min Delay",
+					&(details->item[CMD_CMD].min_delay), true);
+		root = api_add_double(root, "Max Delay",
+					&(details->item[CMD_CMD].max_delay), true);
+		root = api_add_uint64(root, "Timeout Count",
+					&(details->item[CMD_TIMEOUT].count), true);
+		root = api_add_double(root, "Timeout Total Delay",
+					&(details->item[CMD_TIMEOUT].total_delay), true);
+		root = api_add_double(root, "Timeout Min Delay",
+					&(details->item[CMD_TIMEOUT].min_delay), true);
+		root = api_add_double(root, "Timeout Max Delay",
+					&(details->item[CMD_TIMEOUT].max_delay), true);
+		root = api_add_uint64(root, "Error Count",
+					&(details->item[CMD_ERROR].count), true);
+		root = api_add_double(root, "Error Total Delay",
+					&(details->item[CMD_ERROR].total_delay), true);
+		root = api_add_double(root, "Error Min Delay",
+					&(details->item[CMD_ERROR].min_delay), true);
+		root = api_add_double(root, "Error Max Delay",
+					&(details->item[CMD_ERROR].max_delay), true);
+		root = api_add_timeval(root, "First Command",
+					&(details->item[CMD_CMD].first), true);
+		root = api_add_timeval(root, "Last Command",
+					&(details->item[CMD_CMD].last), true);
+		root = api_add_timeval(root, "First Timeout",
+					&(details->item[CMD_TIMEOUT].first), true);
+		root = api_add_timeval(root, "Last Timeout",
+					&(details->item[CMD_TIMEOUT].last), true);
+		root = api_add_timeval(root, "First Error",
+					&(details->item[CMD_ERROR].first), true);
+		root = api_add_timeval(root, "Last Error",
+					&(details->item[CMD_ERROR].last), true);
+
+		return root;
+	}
+#endif
+	return NULL;
+}
+
+#if DO_USB_STATS
+static void newstats(struct cgpu_info *cgpu)
+{
+	int i;
+
+	cgpu->usbstat = ++next_stat;
+
+	usb_stats = realloc(usb_stats, sizeof(*usb_stats) * next_stat);
+	usb_stats[next_stat-1].name = cgpu->api->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);
+	for (i = 1; i < C_MAX * 2; i += 2)
+		usb_stats[next_stat-1].details[i].seq = 1;
+}
+#endif
+
+void update_usb_stats(__maybe_unused struct cgpu_info *cgpu)
+{
+#if DO_USB_STATS
+	// we don't know the device_id until after add_cgpu()
+	usb_stats[cgpu->usbstat - 1].device_id = cgpu->device_id;
+#endif
+}
+
+#if DO_USB_STATS
+static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timeval *tv_finish, int err, enum usb_cmds cmd, int seq)
+{
+	struct cg_usb_stats_details *details;
+	double diff;
+	int item;
+
+	if (cgpu->usbstat < 1)
+		newstats(cgpu);
+
+	details = &(usb_stats[cgpu->usbstat - 1].details[cmd * 2 + seq]);
+
+	diff = tdiff(tv_finish, tv_start);
+
+	switch (err) {
+		case LIBUSB_SUCCESS:
+			item = CMD_CMD;
+			break;
+		case LIBUSB_ERROR_TIMEOUT:
+			item = CMD_TIMEOUT;
+			break;
+		default:
+			item = CMD_ERROR;
+			break;
+	}
+
+	if (details->item[item].count == 0) {
+		details->item[item].min_delay = diff;
+		memcpy(&(details->item[item].first), tv_start, sizeof(*tv_start));
+	} else if (diff < details->item[item].min_delay)
+		details->item[item].min_delay = diff;
+
+	if (diff > details->item[item].max_delay)
+		details->item[item].max_delay = diff;
+
+	details->item[item].total_delay += diff;
+	memcpy(&(details->item[item].last), tv_start, sizeof(tv_start));
+	details->item[item].count++;
+}
+#endif
+
+int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds cmd)
+{
+	struct cg_usb_device *usbdev = cgpu->usbdev;
+#if DO_USB_STATS
+	struct timeval tv_start, tv_finish;
+#endif
+	int err, got, tot;
+	bool first = true;
+
+	if (eol == -1) {
+		got = 0;
+		STATS_TIMEVAL(&tv_start);
+		err = libusb_bulk_transfer(usbdev->handle,
+				usbdev->found->eps[ep].ep,
+				(unsigned char *)buf,
+				bufsiz, &got,
+				timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout);
+		STATS_TIMEVAL(&tv_finish);
+		USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, SEQ0);
+
+		*processed = got;
+
+		return err;
+	}
+
+	tot = 0;
+	while (bufsiz) {
+		got = 0;
+		STATS_TIMEVAL(&tv_start);
+		err = libusb_bulk_transfer(usbdev->handle,
+				usbdev->found->eps[ep].ep,
+				(unsigned char *)buf,
+				1, &got,
+				timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout);
+		STATS_TIMEVAL(&tv_finish);
+		USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, first ? SEQ0 : SEQ1);
+
+		tot += got;
+
+		if (err)
+			break;
+
+		if (eol == buf[0])
+			break;
+
+		buf += got;
+		bufsiz -= got;
+
+		first = false;
+	}
+
+	*processed = tot;
+
+	return err;
+}
+
+int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds cmd)
+{
+	struct cg_usb_device *usbdev = cgpu->usbdev;
+#if DO_USB_STATS
+	struct timeval tv_start, tv_finish;
+#endif
+	int err, sent;
+
+	sent = 0;
+	STATS_TIMEVAL(&tv_start);
+	err = libusb_bulk_transfer(usbdev->handle,
+			usbdev->found->eps[ep].ep,
+			(unsigned char *)buf,
+			bufsiz, &sent,
+			timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout);
+	STATS_TIMEVAL(&tv_finish);
+	USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, SEQ0);
+
+	*processed = sent;
+
+	return err;
+}
+
+void usb_cleanup()
+{
+	// TODO:
+}

+ 120 - 0
usbutils.h

@@ -0,0 +1,120 @@
+/*
+ * Copyright 2012 Andrew Smith
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#ifndef USBUTILS_H
+#define USBUTILS_H
+
+#include <libusb.h>
+
+// Use the device defined timeout
+#define DEVTIMEOUT 0
+
+// For endpoints defined in usb_find_devices.eps,
+// the first two must be the default IN and OUT
+#define DEFAULT_EP_IN 0
+#define DEFAULT_EP_OUT 1
+
+struct usb_endpoints {
+	uint8_t att;
+	uint16_t size;
+	unsigned char ep;
+	bool found;
+};
+
+struct usb_find_devices {
+	int drv;
+	const char *name;
+	uint16_t idVendor;
+	uint16_t idProduct;
+	int config;
+	int interface;
+	unsigned int timeout;
+	int epcount;
+	struct usb_endpoints *eps;
+};
+
+struct cg_usb_device {
+	struct usb_find_devices *found;
+	libusb_device_handle *handle;
+	pthread_mutex_t *mutex;
+	struct libusb_device_descriptor *descriptor;
+	uint8_t bus_number;
+	uint8_t device_address;
+	uint16_t usbver;
+	int speed;
+	char *prod_string;
+	char *manuf_string;
+	char *serial_string;
+	unsigned char fwVersion;	// ??
+	unsigned char interfaceVersion;	// ??
+};
+
+enum usb_cmds {
+	C_PING = 0,
+	C_CLEAR,
+	C_REQUESTVERSION,
+	C_GETVERSION,
+	C_REQUESTFPGACOUNT,
+	C_GETFPGACOUNT,
+	C_STARTPROGRAM,
+	C_STARTPROGRAMSTATUS,
+	C_PROGRAM,
+	C_PROGRAMSTATUS,
+	C_PROGRAMSTATUS2,
+	C_FINALPROGRAMSTATUS,
+	C_SETCLOCK,
+	C_REPLYSETCLOCK,
+	C_REQUESTUSERCODE,
+	C_GETUSERCODE,
+	C_REQUESTTEMPERATURE,
+	C_GETTEMPERATURE,
+	C_SENDWORK,
+	C_SENDWORKSTATUS,
+	C_REQUESTWORKSTATUS,
+	C_GETWORKSTATUS,
+	C_MAX
+};
+
+struct device_api;
+struct cgpu_info;
+
+void usb_uninit(struct cgpu_info *cgpu);
+bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found);
+void usb_detect(struct device_api *api, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *));
+struct api_data *api_usb_stats(int *count);
+void update_usb_stats(struct cgpu_info *cgpu);
+int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds);
+int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds);
+void usb_cleanup();
+
+#define usb_read(cgpu, buf, bufsiz, read, cmd) \
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, -1, cmd)
+
+#define usb_read_ep(cgpu, ep, buf, bufsiz, read, cmd) \
+	_usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, -1, cmd)
+
+#define usb_read_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, -1, cmd)
+
+#define usb_read_ep_timeout(cgpu, ep, buf, bufsiz, read, timeout, cmd) \
+	_usb_read(cgpu, ep, buf, bufsiz, read, timeout, -1, cmd)
+
+#define usb_write(cgpu, buf, bufsiz, wrote, cmd) \
+	_usb_write(cgpu, DEFAULT_EP_OUT, buf, bufsiz, wrote, DEVTIMEOUT, cmd)
+
+#define usb_write_ep(cgpu, ep, buf, bufsiz, wrote, cmd) \
+	_usb_write(cgpu, ep, buf, bufsiz, wrote, DEVTIMEOUT, cmd)
+
+#define usb_write_timeout(cgpu, buf, bufsiz, wrote, timeout, cmd) \
+	_usb_write(cgpu, DEFAULT_EP_OUT, buf, bufsiz, wrote, timeout, cmd)
+
+#define usb_write_ep_timeout(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) \
+	_usb_write(cgpu, ep, buf, bufsiz, wrote, timeout, cmd)
+
+#endif

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