Browse Source

Merge branch 'cg_merges_20121203' into bfgminer

Luke Dashjr 13 years ago
parent
commit
5286e06249
21 changed files with 1777 additions and 248 deletions
  1. 13 0
      API-README
  2. 28 1
      FPGA-README
  3. 3 3
      Makefile.am
  4. 17 0
      NEWS
  5. 1 1
      README
  6. 75 19
      api.c
  7. 9 10
      configure.ac
  8. 32 10
      driver-modminer.c
  9. 1 1
      driver-opencl.c
  10. 4 1
      driver-ztex.c
  11. 1 1
      dynclock.c
  12. 1 1
      findnonce.c
  13. 6 4
      libztex.c
  14. 157 153
      miner.c
  15. 10 2
      miner.h
  16. 15 12
      scrypt.c
  17. 1192 0
      usbutils.c
  18. 120 0
      usbutils.h
  19. 55 27
      util.c
  20. 3 0
      util.h
  21. 34 2
      windows-build.txt

+ 13 - 0
API-README

@@ -332,6 +332,9 @@ The list of requests - a (*) means it requires privileged access - and replies a
                                                        0 to 9999)
                               coinbase-sig (string)
 
+ 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 BFGMiner status window
 
@@ -384,6 +387,16 @@ miner.php - an example web page to access the API
 Feature Changelog for external applications using the API:
 
 
+API V1.21
+
+Added API commands:
+ 'usbstats'
+
+Modified API commands:
+ 'summary' - add 'Best Share'
+
+----------
+
 API V1.20b (BFGMiner v2.9.1)
 
 Support for the X6500 FPGA was added

+ 28 - 1
FPGA-README

@@ -6,7 +6,34 @@ ModMinerQuad (MMQ)
 ------------------
 
 The mining bitstream does not survive a power cycle, so BFGMiner will upload
-it, if it needs to, before it starts mining
+it, if it needs to, before it starts mining (approx 3min)
+
+The red LED also flashes while it is uploading the bitstream
+
+-
+
+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
 
 -
 

+ 3 - 3
Makefile.am

@@ -8,7 +8,7 @@ EXTRA_DIST	= example.conf m4/gnulib-cache.m4 linux-usb-bfgminer \
 
 SUBDIRS		= lib ccan
 
-INCLUDES	= $(PTHREAD_FLAGS) -fno-strict-aliasing $(USB_CFLAGS)
+INCLUDES	= $(PTHREAD_FLAGS) -fno-strict-aliasing
 
 bin_PROGRAMS	= bfgminer
 
@@ -17,9 +17,9 @@ bin_SCRIPTS	= *.cl
 bfgminer_LDFLAGS	= $(PTHREAD_FLAGS)
 bfgminer_LDADD	= $(DLOPEN_FLAGS) @LIBCURL_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ \
 		  @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \
-		  @UDEV_LIBS@ @USB_LIBS@ \
+		  @UDEV_LIBS@ @LIBUSB_LIBS@ \
 		  @MATH_LIBS@ lib/libgnu.a ccan/libccan.a
-bfgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @LIBCURL_CFLAGS@
+bfgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @LIBUSB_CFLAGS@ @LIBCURL_CFLAGS@
 
 bfgminer_CPPFLAGS += $(NCURSES_CPPFLAGS)
 

+ 17 - 0
NEWS

@@ -1,5 +1,22 @@
 BFGMiner Version 2.10.0 - Future
 
+- Make gen_stratum_work more robust by using a dynamically allocated array for
+the header in case bogus data is sent by the pool to avoid overflowing a static
+array.
+- scrypt_diff now returns a uint64_t
+- Support monitoring and reporting much higher diffs for scrypt mining,
+truncating irrelevant zeroes from displayed hash.
+- Pass ostate values around in scrypt to be able to extract full hashes if
+needed later on.
+- Revert "Handle crash exceptions by trying to restart cgminer unless the
+--no-restart option is used."
+- Count longpoll decodes as queued work since the count otherwise remains
+static.
+- Provide helper function realloc_strcat to extend arbitrary length arrays
+based on string length.
+- Bugfix: Assign header-based rolltime before decoding work, so GBT expires
+overrides it properly
+- Bugfix: FPGA-README: Correct idVendor in example MMQ udev rule
 - fixes target calc for mips openwrt
 - openwrt needs roundl
 - Get rid of unused last_work in opencl thread data.

+ 1 - 1
README

@@ -141,7 +141,7 @@ Options for both config file and command line:
 --no-gbt            Disable getblocktemplate support
 --no-longpoll       Disable X-Long-Polling support
 --no-pool-disable   Do not automatically disable pools that continually reject shares
---no-restart        Do not attempt to restart devices that hang or BFGMiner if it crashes
+--no-restart        Do not attempt to restart devices that hang
 --no-stratum        Disable Stratum detection
 --no-submit-stale   Don't submit shares if they are detected as stale
 --pass|-p <arg>     Password for bitcoin JSON-RPC server

+ 75 - 19
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";
 static const char *SICK = "Sick";
 static const char *NOSTART = "NoStart";
@@ -231,6 +231,7 @@ static const char *OSINFO =
 #define _MINECOIN	"COIN"
 #define _DEBUGSET	"DEBUG"
 #define _SETCONFIG	"SETCONFIG"
+#define _USBSTATS	"USBSTATS"
 
 static const char ISJSON = '{';
 #define JSON0		"{"
@@ -270,6 +271,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";
@@ -374,6 +376,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,
@@ -538,6 +542,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 }
 };
 
@@ -1786,6 +1792,9 @@ static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo
 		algo = (char *)NULLSTR;
 #endif
 
+	// stop hashmeter() changing some while copying
+	mutex_lock(&hash_lock);
+
 	utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60;
 	mhs = total_mhashes_done / total_secs;
 	work_utility = total_diff1 / ( total_secs ? total_secs : 1 ) * 60;
@@ -1795,29 +1804,32 @@ static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo
 		: "%s" _SUMMARY ",",
 		message(MSG_SUMM, 0, NULL, isjson));
 
-	root = api_add_elapsed(root, "Elapsed", &(total_secs), false);
+	root = api_add_elapsed(root, "Elapsed", &(total_secs), true);
 #ifdef WANT_CPUMINE
 	if (opt_n_threads)
 	root = api_add_string(root, "Algorithm", algo, false);
 #endif
 	root = api_add_mhs(root, "MHS av", &(mhs), false);
-	root = api_add_uint(root, "Found Blocks", &(found_blocks), false);
-	root = api_add_int(root, "Getworks", &(total_getworks), false);
-	root = api_add_int(root, "Accepted", &(total_accepted), false);
-	root = api_add_int(root, "Rejected", &(total_rejected), false);
-	root = api_add_int(root, "Hardware Errors", &(hw_errors), false);
+	root = api_add_uint(root, "Found Blocks", &(found_blocks), true);
+	root = api_add_int(root, "Getworks", &(total_getworks), true);
+	root = api_add_int(root, "Accepted", &(total_accepted), true);
+	root = api_add_int(root, "Rejected", &(total_rejected), true);
+	root = api_add_int(root, "Hardware Errors", &(hw_errors), true);
 	root = api_add_utility(root, "Utility", &(utility), false);
-	root = api_add_int(root, "Discarded", &(total_discarded), false);
-	root = api_add_int(root, "Stale", &(total_stale), false);
-	root = api_add_uint(root, "Get Failures", &(total_go), false);
-	root = api_add_uint(root, "Local Work", &(local_work), false);
-	root = api_add_uint(root, "Remote Failures", &(total_ro), false);
-	root = api_add_uint(root, "Network Blocks", &(new_blocks), false);
-	root = api_add_mhtotal(root, "Total MH", &(total_mhashes_done), false);
+	root = api_add_int(root, "Discarded", &(total_discarded), true);
+	root = api_add_int(root, "Stale", &(total_stale), true);
+	root = api_add_uint(root, "Get Failures", &(total_go), true);
+	root = api_add_uint(root, "Local Work", &(local_work), true);
+	root = api_add_uint(root, "Remote Failures", &(total_ro), true);
+	root = api_add_uint(root, "Network Blocks", &(new_blocks), true);
+	root = api_add_mhtotal(root, "Total MH", &(total_mhashes_done), true);
 	root = api_add_utility(root, "Work Utility", &(work_utility), false);
-	root = api_add_diff(root, "Difficulty Accepted", &(total_diff_accepted), false);
-	root = api_add_diff(root, "Difficulty Rejected", &(total_diff_rejected), false);
-	root = api_add_diff(root, "Difficulty Stale", &(total_diff_stale), false);
+	root = api_add_diff(root, "Difficulty Accepted", &(total_diff_accepted), true);
+	root = api_add_diff(root, "Difficulty Rejected", &(total_diff_rejected), true);
+	root = api_add_diff(root, "Difficulty Stale", &(total_diff_stale), true);
+	root = api_add_uint64(root, "Best Share", &(best_diff), true);
+
+	mutex_unlock(&hash_lock);
 
 	root = print_data(root, buf, isjson);
 	if (isjson)
@@ -2849,6 +2861,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_USBUTILS
+	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_USBUTILS
+
+	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 {
@@ -2905,6 +2960,7 @@ struct CMDS {
 	{ "coin",		minecoin,	false },
 	{ "debug",		debugstate,	true },
 	{ "setconfig",		setconfig,	true },
+	{ "usbstats",		usbstats,	false },
 	{ NULL,			NULL,		false }
 };
 
@@ -3252,7 +3308,7 @@ popipo:
 
 static void *quit_thread(__maybe_unused void *userdata)
 {
-	rename_thr("bfg-rpc-quit");
+	RenameThread("rpc_quit");
 
 	// allow thread creator to finish whatever it's doing
 	mutex_lock(&quit_restart_lock);
@@ -3268,7 +3324,7 @@ static void *quit_thread(__maybe_unused void *userdata)
 
 static void *restart_thread(__maybe_unused void *userdata)
 {
-	rename_thr("bfg-rpc-restart");
+	RenameThread("rpc_restart");
 
 	// allow thread creator to finish whatever it's doing
 	mutex_lock(&quit_restart_lock);

+ 9 - 10
configure.ac

@@ -65,8 +65,6 @@ AC_FUNC_ALLOCA
 have_cygwin=false
 have_win32=false
 PTHREAD_FLAGS="-lpthread"
-USB_LIBS=""
-USB_CFLAGS=""
 DLOPEN_FLAGS="-ldl"
 WS2_LIBS=""
 MATH_LIBS="-lm"
@@ -185,7 +183,7 @@ PKG_PROG_PKG_CONFIG()
 
 libusb=no
 libusb_include_path=""
-PKG_CHECK_MODULES([USB], [libusb-1.0],[
+PKG_CHECK_MODULES([LIBUSB], [libusb-1.0],[
 	libusb=yes
 ],[
 	for usb_lib in usb-1.0 usb; do
@@ -211,9 +209,9 @@ PKG_CHECK_MODULES([USB], [libusb-1.0],[
 	fi
 ])
 if test "x$libusb" = xyes; then
-	AC_DEFINE([HAVE_LIBUSB], [1], [Defined to 1 if libusb is wanted])
+	AC_DEFINE([HAVE_LIBUSB], [1], [Define if you have libusb-1.0])
 	save_CFLAGS="$CFLAGS"
-	CFLAGS="$USB_CFLAGS $CFLAGS"
+	CFLAGS="$LIBUSB_CFLAGS $CFLAGS"
 	AC_CHECK_DECLS([libusb_error_name],[],[],[#include <libusb.h>])
 	CFLAGS="$save_CFLAGS"
 fi
@@ -398,6 +396,9 @@ if test "x$bitforce$modminer" != xnono; then
 fi
 AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno])
 
+AC_SUBST(LIBUSB_LIBS)
+AC_SUBST(LIBUSB_CFLAGS)
+
 PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.18.2], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.18.2])])
 AC_SUBST(LIBCURL_LIBS)
 
@@ -456,8 +457,6 @@ AC_SUBST(PDCURSES_LIBS)
 AC_SUBST(WS2_LIBS)
 AC_SUBST(MATH_LIBS)
 AC_SUBST(UDEV_LIBS)
-AC_SUBST(USB_LIBS)
-AC_SUBST(USB_CFLAGS)
 
 AC_CONFIG_FILES([
 	Makefile
@@ -552,9 +551,9 @@ fi
 echo
 echo "Compilation............: make (or gmake)"
 echo "  CPPFLAGS.............: $CPPFLAGS $NCURSES_CPPFLAGS"
-echo "  CFLAGS...............: $CFLAGS"
-echo "  LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS $USB_CFLAGS"
-echo "  LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $UDEV_LIBS $USB_LIBS"
+echo "  CFLAGS...............: $CFLAGS $LIBUSB_CFLAGS"
+echo "  LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS"
+echo "  LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $UDEV_LIBS $LIBUSB_LIBS"
 echo
 echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')"
 echo "  prefix...............: $prefix"

+ 32 - 10
driver-modminer.c

@@ -16,15 +16,34 @@
 #include "dynclock.h"
 #include "logging.h"
 #include "miner.h"
+#include "usbutils.h"
 #include "fpgautils.h"
 #include "util.h"
 
 #define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.bit"
 #define BISTREAM_USER_ID "\2\4$B"
+
 #define MODMINER_MINIMUM_CLOCK    2
 #define MODMINER_DEFAULT_CLOCK  200
 #define MODMINER_MAXIMUM_CLOCK  210
 
+// Commands
+#define MODMINER_PING "\x00"
+#define MODMINER_GET_VERSION "\x01"
+#define MODMINER_FPGA_COUNT "\x02"
+// Commands + require FPGAid
+#define MODMINER_GET_IDCODE '\x03'
+#define MODMINER_GET_USERCODE '\x04'
+#define MODMINER_PROGRAM '\x05'
+#define MODMINER_SET_CLOCK '\x06'
+#define MODMINER_READ_CLOCK '\x07'
+#define MODMINER_SEND_WORK '\x08'
+#define MODMINER_CHECK_WORK '\x09'
+// One byte temperature reply
+#define MODMINER_TEMP1 '\x0a'
+
+#define FPGAID_ALL 4
+
 struct device_api modminer_api;
 
 struct modminer_fpga_state {
@@ -68,6 +87,9 @@ _bailout(int fd, struct cgpu_info*modminer, int prio, const char *fmt, ...)
 }
 #define bailout(...)  return _bailout(fd, NULL, __VA_ARGS__);
 
+// 45 noops sent when detecting, in case the device was left in "start job" reading
+static const char NOOP[] = MODMINER_PING "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
+
 static bool
 modminer_detect_one(const char *devpath)
 {
@@ -80,11 +102,11 @@ modminer_detect_one(const char *devpath)
 
 	// Sending a "ping" first, to workaround bug in new firmware betas (see issue #62)
 	// Sending 45 noops, just in case the device was left in "start job" reading
-	(void)(write(fd, "\0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 46) ?:0);
+	(void)(write(fd, NOOP, sizeof(NOOP)) ?:0);
 	while (serial_read(fd, buf, sizeof(buf)) > 0)
 		;
 
-	if (1 != write(fd, "\x01", 1))  // Get version
+	if (1 != write(fd, MODMINER_GET_VERSION, 1))
 		bailout(LOG_DEBUG, "ModMiner detect: write failed on %s (get version)", devpath);
 	len = serial_read(fd, buf, sizeof(buf)-1);
 	if (len < 1)
@@ -93,7 +115,7 @@ modminer_detect_one(const char *devpath)
 	char*devname = strdup(buf);
 	applog(LOG_DEBUG, "ModMiner identified as: %s", devname);
 
-	if (1 != write(fd, "\x02", 1))  // Get FPGA count
+	if (1 != write(fd, MODMINER_FPGA_COUNT, 1))
 		bailout(LOG_DEBUG, "ModMiner detect: write failed on %s (get FPGA count)", devpath);
 	len = read(fd, buf, 1);
 	if (len < 1)
@@ -194,7 +216,7 @@ modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
 	fd_set fds;
 	char buf[0x100];
 	unsigned long len, flen;
-	char fpgaid = 4;  // "all FPGAs"
+	char fpgaid = FPGAID_ALL;
 	FILE *f = open_xilinx_bitstream(modminer, BITSTREAM_FILENAME, &len);
 	if (!f)
 		return false;
@@ -203,7 +225,7 @@ modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
 	int fd = modminer->device_fd;
 
 	applog(LOG_WARNING, "%s %u: Programming %s... DO NOT EXIT UNTIL COMPLETE", modminer->api->name, modminer->device_id, modminer->device_path);
-	buf[0] = '\x05';  // Program Bitstream
+	buf[0] = MODMINER_PROGRAM;
 	buf[1] = fpgaid;
 	buf[2] = (len >>  0) & 0xff;
 	buf[3] = (len >>  8) & 0xff;
@@ -266,7 +288,7 @@ modminer_fpga_prepare(struct thr_info *thr)
 	struct modminer_fpga_state *state;
 	state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state));
 	dclk_prepare(&state->dclk);
-	state->next_work_cmd[0] = '\x08';  // Send Job
+	state->next_work_cmd[0] = MODMINER_SEND_WORK;
 	state->next_work_cmd[1] = thr->device_thread;  // FPGA id
 
 	return true;
@@ -284,7 +306,7 @@ modminer_change_clock(struct thr_info*thr, bool needlock, signed char delta)
 
 	clk = (state->dclk.freqM * 2) + delta;
 
-	cmd[0] = '\x06';  // set frequency
+	cmd[0] = MODMINER_SET_CLOCK;
 	cmd[1] = fpgaid;
 	cmd[2] = clk;
 	cmd[3] = cmd[4] = cmd[5] = '\0';
@@ -335,7 +357,7 @@ modminer_reduce_clock(struct thr_info*thr, bool needlock)
 static bool _modminer_get_nonce(struct cgpu_info*modminer, char fpgaid, uint32_t*nonce)
 {
 	int fd = modminer->device_fd;
-	char cmd[2] = {'\x09', fpgaid};
+	char cmd[2] = {MODMINER_CHECK_WORK, fpgaid};
 	
 	if (write(fd, cmd, 2) != 2) {
 		applog(LOG_ERR, "%s %u: Error writing (get nonce %u)", modminer->api->name, modminer->device_id, fpgaid);
@@ -368,7 +390,7 @@ modminer_fpga_init(struct thr_info *thr)
 		return false;
 	}
 
-	cmd[0] = '\x04';  // Read USER code (bitstream id)
+	cmd[0] = MODMINER_GET_USERCODE;
 	cmd[1] = fpgaid;
 	if (write(fd, cmd, 2) != 2)
 		bailout2(LOG_ERR, "%s %u.%u: Error writing (read USER code)", modminer->api->name, modminer->device_id, fpgaid);
@@ -473,7 +495,7 @@ static void modminer_get_temperature(struct cgpu_info *modminer, struct thr_info
 
 	int fd = modminer->device_fd;
 	int fpgaid = thr->device_thread;
-	char cmd[2] = {'\x0a', fpgaid};
+	char cmd[2] = {MODMINER_TEMP1, fpgaid};
 	char temperature;
 
 	if (2 == write(fd, cmd, 2) && read(fd, &temperature, 1) == 1)

+ 1 - 1
driver-opencl.c

@@ -1329,7 +1329,7 @@ void *reinit_gpu(void *userdata)
 	int gpu;
 
 	pthread_detach(pthread_self());
-	rename_thr("bfg-reinit_gpu");
+	RenameThread("reinit_gpu");
 
 select_cgpu:
 	cgpu = tq_pop(mythr->q, NULL);

+ 4 - 1
driver-ztex.c

@@ -365,8 +365,11 @@ static bool ztex_prepare(struct thr_info *thr)
 	get_datestamp(cgpu->init, &now);
 	
 	ztex_selectFpga(ztex);
-	if (libztex_configureFpga(ztex) != 0)
+	if (libztex_configureFpga(ztex) != 0) {
+		libztex_resetFpga(ztex);
+		ztex_releaseFpga(ztex);
 		return false;
+	}
 	ztex_releaseFpga(ztex);
 	ztex->dclk.freqM = ztex->dclk.freqMaxM+1;;
 	//ztex_updateFreq(thr);

+ 1 - 1
dynclock.c

@@ -20,7 +20,7 @@ void dclk_prepare(struct dclk_data *data)
 
 void dclk_msg_freqchange(const char *repr, int oldFreq, int newFreq, const char *tail)
 {
-	applog(LOG_NOTICE, "%s: Frequency %s from %u to %u Mhz%s",
+	applog(LOG_NOTICE, "%s: Frequency %s from %u to %u MHz%s",
 	       repr,
 	       (oldFreq > newFreq ? "dropped" : "raised "),
 	       oldFreq, newFreq,

+ 1 - 1
findnonce.c

@@ -147,7 +147,7 @@ static void *postcalc_hash(void *userdata)
 	unsigned int entry = 0;
 
 	pthread_detach(pthread_self());
-	rename_thr("bfg-postcalchsh");
+	RenameThread("postcalchsh");
 
 	/* To prevent corrupt values in FOUND from trying to read beyond the
 	 * end of the res[] array */

+ 6 - 4
libztex.c

@@ -307,11 +307,13 @@ static int libztex_configureFpgaLS(struct libztex_device *ztex, const char* firm
 
 		fclose(fp);
 	}
+
 	libztex_getFpgaState(ztex, &state);
 	if (!state.fpgaConfigured) {
-		applog(LOG_ERR, "%s: FPGA configuration failed: DONE pin does not go high", ztex->repr);
-		return 3;
+		applog(LOG_ERR, "%s: LS FPGA configuration failed: DONE pin does not go high", ztex->repr);
+		return -3;
 	}
+
 	nmsleep(200);
 	applog(LOG_INFO, "%s: FPGA configuration done", ztex->repr);
 	return 0;
@@ -384,7 +386,7 @@ int libztex_setFreq(struct libztex_device *ztex, uint16_t freq) {
 	}
 	ztex->dclk.freqM = freq;
 	if (oldfreq > ztex->dclk.freqMaxM)
-		applog(LOG_WARNING, "%s: Frequency set to %u Mhz (range: %u-%u)",
+		applog(LOG_WARNING, "%s: Frequency set to %u MHz (range: %u-%u)",
 		       ztex->repr,
 		       (unsigned)(ztex->freqM1 * (ztex->dclk.freqM + 1)),
 		       (unsigned)ztex->freqM1,
@@ -474,7 +476,7 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 	}
 
 	/* num chars = (all bytes except bLength and bDescriptorType) / 2 */
-	for (i = 0; i <= (cnt - 2) / 2 && i < sizeof(newdev->snString)-1; i++)
+	for (i = 0; i <= (cnt - 2) / 2 && i < (int)sizeof(newdev->snString)-1; i++)
 		newdev->snString[i] = buf[2 + i*2];
 
 	newdev->snString[i] = 0;

+ 157 - 153
miner.c

@@ -183,6 +183,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;
@@ -204,7 +207,11 @@ int gpur_thr_id;
 static int api_thr_id;
 static int total_threads;
 
-static pthread_mutex_t hash_lock;
+#ifdef HAVE_LIBUSB
+pthread_mutex_t cgusb_lock;
+#endif
+
+pthread_mutex_t hash_lock;
 static pthread_mutex_t qd_lock;
 static pthread_mutex_t *stgd_lock;
 pthread_mutex_t console_lock;
@@ -263,7 +270,7 @@ static char datestamp[40];
 static char blocktime[32];
 struct timeval block_timeval;
 static char best_share[8] = "0";
-static uint64_t best_diff = 0;
+uint64_t best_diff = 0;
 
 struct block {
 	char hash[40];
@@ -305,7 +312,7 @@ static int include_count;
 
 bool ping = true;
 
-struct sigaction termhandler, inthandler, segvhandler, bushandler, illhandler;
+struct sigaction termhandler, inthandler;
 
 struct thread_q *getq;
 
@@ -1229,7 +1236,7 @@ static struct opt_table opt_config_table[] = {
 			"Do not automatically disable pools that continually reject shares"),
 	OPT_WITHOUT_ARG("--no-restart",
 			opt_set_invbool, &opt_restart,
-			"Do not attempt to restart devices that hang or BFGMiner if it crashes"
+			"Do not attempt to restart devices that hang"
 	),
 	OPT_WITHOUT_ARG("--no-stratum",
 			opt_set_invbool, &want_stratum,
@@ -1357,6 +1364,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,
@@ -1631,6 +1643,51 @@ static void calc_midstate(struct work *work)
 	swap32tole(work->midstate, work->midstate, 8);
 }
 
+static struct work *make_work(void)
+{
+	struct work *work = calloc(1, sizeof(struct work));
+
+	if (unlikely(!work))
+		quit(1, "Failed to calloc work in make_work");
+	mutex_lock(&control_lock);
+	work->id = total_work++;
+	mutex_unlock(&control_lock);
+	return work;
+}
+
+/* This is the central place all work that is about to be retired should be
+ * cleaned to remove any dynamically allocated arrays within the struct */
+void clean_work(struct work *work)
+{
+	free(work->job_id);
+	free(work->nonce2);
+	free(work->ntime);
+	work->job_id = NULL;
+	work->nonce2 = NULL;
+	work->ntime = NULL;
+
+	if (work->tmpl) {
+		struct pool *pool = work->pool;
+		mutex_lock(&pool->pool_lock);
+		bool free_tmpl = !--*work->tmpl_refcount;
+		mutex_unlock(&pool->pool_lock);
+		if (free_tmpl) {
+			blktmpl_free(work->tmpl);
+			free(work->tmpl_refcount);
+		}
+		work->tmpl = NULL;
+		work->tmpl_refcount = NULL;
+	}
+}
+
+/* All dynamically allocated work structs should be freed here to not leak any
+ * ram from arrays allocated within the work struct */
+void free_work(struct work *work)
+{
+	clean_work(work);
+	free(work);
+}
+
 static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 {
 	json_t *res_val = json_object_get(val, "result");
@@ -2524,14 +2581,14 @@ static uint64_t share_diff(const struct work *work)
 	return ret;
 }
 
-static uint32_t scrypt_diff(const struct work *work)
+static uint64_t scrypt_diff(const struct work *work)
 {
-	const uint32_t scrypt_diffone = 0x0000fffful;
-	uint32_t d32 = work->outputhash, ret;
+	const uint64_t scrypt_diffone = 0x0000ffff00000000ul;
+	uint64_t d64 = work->outputhash, ret;
 
-	if (unlikely(!d32))
-		d32 = 1;
-	ret = scrypt_diffone / d32;
+	if (unlikely(!d64))
+		d64 = 1;
+	ret = scrypt_diffone / d64;
 	if (ret > best_diff) {
 		best_diff = ret;
 		suffix_string(best_diff, best_share, 0);
@@ -2562,30 +2619,32 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit)
 		json_decref(req);
 		sd = bin2hex(data, 80);
 	} else {
-		s  = malloc(345);
 		sd = s;
 
 	/* build hex string */
 	hexstr = bin2hex(work->data, sizeof(work->data));
 
 	/* build JSON-RPC request */
-		sprintf(s, "{\"method\": \"getwork\", \"params\": [ \"%s\" ], \"id\":1}", hexstr);
+		s = strdup("{\"method\": \"getwork\", \"params\": [ \"");
+		s = realloc_strcat(s, hexstr);
+		s = realloc_strcat(s, "\" ], \"id\":1}");
 
 	}
 
 	applog(LOG_DEBUG, "DBG: sending %s submit RPC call: %s", pool->rpc_url, sd);
 	if (!work->tmpl)
-	strcat(s, "\n");
+	s = realloc_strcat(s, "\n");
 
 	gettimeofday(&tv_submit, NULL);
 	/* issue JSON-RPC request */
 	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, false, false, &rolltime, pool, true);
 
-	free(s);
 	if (sd != s)
 	free(sd);
 
 	gettimeofday(&tv_submit_reply, NULL);
+	free(s);
+
 	if (unlikely(!val)) {
 		applog(LOG_INFO, "submit_upstream_work json_rpc_call failed");
 		if (!pool_tset(pool, &pool->submit_fail)) {
@@ -2608,12 +2667,14 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit)
 		hash32 = (uint32_t *)(work->hash);
 		if (opt_scrypt) {
 			uint32_t sharediff;
+			uint64_t outhash;
 
 			scrypt_outputhash(work);
 			sharediff = scrypt_diff(work);
 			suffix_string(sharediff, diffdisp, 0);
 
-			sprintf(hashshow, "%08lx Diff %s/%d", (unsigned long)work->outputhash, diffdisp, intdiff);
+			outhash = work->outputhash >> 16;
+			sprintf(hashshow, "%08lx Diff %s/%d", (unsigned long)outhash, diffdisp, intdiff);
 		} else {
 			uint64_t sharediff = share_diff(work);
 
@@ -2941,51 +3002,6 @@ tryagain:
 	return rc;
 }
 
-static struct work *make_work(void)
-{
-	struct work *work = calloc(1, sizeof(struct work));
-
-	if (unlikely(!work))
-		quit(1, "Failed to calloc work in make_work");
-	mutex_lock(&control_lock);
-	work->id = total_work++;
-	mutex_unlock(&control_lock);
-	return work;
-}
-
-/* This is the central place all work that is about to be retired should be
- * cleaned to remove any dynamically allocated arrays within the struct */
-void clean_work(struct work *work)
-{
-	free(work->job_id);
-	free(work->nonce2);
-	free(work->ntime);
-	work->job_id = NULL;
-	work->nonce2 = NULL;
-	work->ntime = NULL;
-
-	if (work->tmpl) {
-		struct pool *pool = work->pool;
-		mutex_lock(&pool->pool_lock);
-		bool free_tmpl = !--*work->tmpl_refcount;
-		mutex_unlock(&pool->pool_lock);
-		if (free_tmpl) {
-			blktmpl_free(work->tmpl);
-			free(work->tmpl_refcount);
-		}
-		work->tmpl = NULL;
-		work->tmpl_refcount = NULL;
-	}
-}
-
-/* All dynamically allocated work structs should be freed here to not leak any
- * ram from arrays allocated within the work struct */
-void free_work(struct work *work)
-{
-	clean_work(work);
-	free(work);
-}
-
 static void workio_cmd_free(struct workio_cmd *wc)
 {
 	if (!wc)
@@ -3099,8 +3115,13 @@ char **initial_args;
 
 static void clean_up(void);
 
-static inline void __app_restart(void)
+void app_restart(void)
 {
+	applog(LOG_WARNING, "Attempting to restart %s", packagename);
+
+	__kill_work();
+	clean_up();
+
 #if defined(unix)
 	if (forkpid > 0) {
 		kill(forkpid, SIGTERM);
@@ -3109,55 +3130,17 @@ static inline void __app_restart(void)
 #endif
 
 	execv(initial_args[0], initial_args);
-}
-
-void app_restart(void)
-{
-	applog(LOG_WARNING, "Attempting to restart %s", packagename);
-
-	__kill_work();
-	clean_up();
-
-	__app_restart();
-
-	/* We shouldn't reach here */
 	applog(LOG_WARNING, "Failed to restart application");
 }
 
-/* Returns all signal handlers to their defaults */
-static inline void __sighandler(void)
+static void sighandler(int __maybe_unused sig)
 {
 	/* Restore signal handlers so we can still quit if kill_work fails */
 	sigaction(SIGTERM, &termhandler, NULL);
 	sigaction(SIGINT, &inthandler, NULL);
-	if (opt_restart) {
-		sigaction(SIGSEGV, &segvhandler, NULL);
-		sigaction(SIGILL, &illhandler, NULL);
-#ifndef WIN32
-		sigaction(SIGBUS, &bushandler, NULL);
-#endif
-	}
-}
-
-static void sighandler(int __maybe_unused sig)
-{
-	__sighandler();
 	kill_work();
 }
 
-/* Handles segfaults and other crashes by attempting to restart cgminer. Try to
- * do as little as possible since we are probably corrupted. */
-static void seghandler(int sig)
-{
-	__sighandler();
-	fprintf(stderr, "\nCrashed with signal %d! Will attempt to restart\n", sig);
-	__app_restart();
-	/* We shouldn't reach here */
-	fprintf(stderr, "Failed to restart, exiting now\n");
-
-	exit(1);
-}
-
 static void start_longpoll(void);
 static void stop_longpoll(void);
 
@@ -3427,7 +3410,8 @@ static void *get_work_thread(void *userdata)
 	struct pool *pool;
 
 	pthread_detach(pthread_self());
-	rename_thr("bfg-get_work");
+
+	RenameThread("get_work");
 
 	applog(LOG_DEBUG, "Creating extra get work thread");
 
@@ -3645,7 +3629,8 @@ static void *submit_work_thread(void *userdata)
 	time_t staleexpire;
 
 	pthread_detach(pthread_self());
-	rename_thr("bfg-submit_work");
+
+	RenameThread("submit_work");
 
 	applog(LOG_DEBUG, "Creating extra submit work thread");
 
@@ -4327,7 +4312,7 @@ static void *stage_thread(void *userdata)
 	struct thr_info *mythr = userdata;
 	bool ok = true;
 
-	rename_thr("bfg-stage");
+	RenameThread("stage");
 
 	while (ok) {
 		struct work *work = NULL;
@@ -5063,7 +5048,7 @@ retry:
 
 static void *input_thread(void __maybe_unused *userdata)
 {
-	rename_thr("bfg-input");
+	RenameThread("input");
 
 	if (!curses_active)
 		return NULL;
@@ -5125,7 +5110,7 @@ static void *workio_thread(void *userdata)
 	struct thr_info *mythr = userdata;
 	bool ok = true;
 
-	rename_thr("bfg-workio");
+	RenameThread("work_io");
 
 	while (ok) {
 		struct workio_cmd *wc;
@@ -5179,9 +5164,10 @@ static void *api_thread(void *userdata)
 	struct thr_info *mythr = userdata;
 
 	pthread_detach(pthread_self());
-	rename_thr("bfg-rpc");
 	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 
+	RenameThread("rpc");
+
 	api(api_thr_id);
 
 	PTH(mythr) = 0L;
@@ -5465,6 +5451,26 @@ static void clear_stratum_shares(struct pool *pool)
 	}
 }
 
+/* We only need to maintain a secondary pool connection when we need the
+ * capacity to get work from the backup pools while still on the primary */
+static bool cnx_needed(struct pool *pool)
+{
+	struct pool *cp;
+
+	if (pool_strategy == POOL_BALANCE)
+		return true;
+	if (pool_strategy == POOL_LOADBALANCE)
+		return true;
+	cp = current_pool();
+	if (cp == pool)
+		return true;
+	if (!cp->has_stratum && (!opt_fail_only || !cp->hdr_path))
+		return true;
+	return false;
+}
+
+static void wait_lpcurrent(struct pool *pool);
+
 /* One stratum thread per pool that has stratum waits on the socket checking
  * for new messages and for the integrity of the socket connection. We reset
  * the connection based on the integrity of the receive side only as the send
@@ -5475,6 +5481,8 @@ static void *stratum_thread(void *userdata)
 
 	pthread_detach(pthread_self());
 
+	RenameThread("stratum");
+
 	while (42) {
 		struct timeval timeout;
 		fd_set rd;
@@ -5483,6 +5491,22 @@ static void *stratum_thread(void *userdata)
 		if (unlikely(!pool->has_stratum))
 			break;
 
+		/* Check to see whether we need to maintain this connection
+		 * indefinitely or just bring it up when we switch to this
+		 * pool */
+		if (!cnx_needed(pool)) {
+			suspend_stratum(pool);
+			wait_lpcurrent(pool);
+			if (!initiate_stratum(pool) || !auth_stratum(pool)) {
+				pool_died(pool);
+				while (!initiate_stratum(pool) || !auth_stratum(pool)) {
+					if (pool->removed)
+						goto out;
+					sleep(30);
+				}
+			}
+		}
+
 		FD_ZERO(&rd);
 		FD_SET(pool->sock, &rd);
 		timeout.tv_sec = 120;
@@ -5983,7 +6007,7 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 	unsigned char *coinbase, merkle_root[32], merkle_sha[64], *merkle_hash;
 	int len, cb1_len, n1_len, cb2_len, i;
 	uint32_t *data32, *swap32;
-	char header[260];
+	char *header;
 
 	mutex_lock(&pool->pool_lock);
 
@@ -6017,13 +6041,13 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 		swap32[i] = swab32(data32[i]);
 	merkle_hash = (unsigned char *)bin2hex((const unsigned char *)merkle_root, 32);
 
-	sprintf(header, "%s", pool->swork.bbversion);
-	strcat(header, pool->swork.prev_hash);
-	strcat(header, (char *)merkle_hash);
-	strcat(header, pool->swork.ntime);
-	strcat(header, pool->swork.nbit);
-	strcat(header, "00000000"); /* nonce */
-	strcat(header, "000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000");
+	header = strdup(pool->swork.bbversion);
+	header = realloc_strcat(header, pool->swork.prev_hash);
+	header = realloc_strcat(header, (char *)merkle_hash);
+	header = realloc_strcat(header, pool->swork.ntime);
+	header = realloc_strcat(header, pool->swork.nbit);
+	header = realloc_strcat(header, "00000000"); /* nonce */
+	header = realloc_strcat(header, "000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000");
 
 	/* Store the stratum work diff to check it still matches the pool's
 	 * stratum diff when submitting shares */
@@ -6044,6 +6068,7 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 	/* Convert hex data to binary data for work */
 	if (unlikely(!hex2bin(work->data, header, 128)))
 		quit(1, "Failed to convert header to data in gen_stratum_work");
+	free(header);
 	calc_midstate(work);
 
 	set_work_target(work, work->sdiff);
@@ -6196,7 +6221,6 @@ enum test_nonce2_result hashtest2(struct work *work, bool checktarget)
 	unsigned char hash1[32];
 	unsigned char hash2[32];
 	uint32_t *hash2_32 = (uint32_t *)hash2;
-	struct pool *pool = work->pool;
 
 	swap32yes(swap32, data32, 80 / 4);
 
@@ -6213,21 +6237,6 @@ enum test_nonce2_result hashtest2(struct work *work, bool checktarget)
 
 	memcpy((void*)work->hash, hash2, 32);
 
-	if (work->stratum) {
-		double diff;
-
-		mutex_lock(&pool->pool_lock);
-		diff = pool->swork.diff;
-		mutex_unlock(&pool->pool_lock);
-
-		/* Retarget share only if pool diff has dropped since we
-		 * generated this work */
-		if (unlikely(work->sdiff > diff)) {
-			applog(LOG_DEBUG, "Share needs retargetting to match pool");
-			set_work_target(work, diff);
-		}
-	}
-
 	if (!fulltest(work->hash, work->target))
 		return TNR_HIGH;
 
@@ -6326,12 +6335,6 @@ void *miner_thread(void *userdata)
 	struct cgminer_stats *pool_stats;
 	struct timeval getwork_start;
 
-	{
-		char thrname[16];
-		sprintf(thrname, "bfg-miner-%s%d", api->name, cgpu->device_id);
-		rename_thr(thrname);
-	}
-
 	/* Try to cycle approximately 5 times before each log update */
 	const long cycle = opt_log_interval / 5 ? : 1;
 	struct timeval tv_start, tv_end, tv_workstart, tv_lastupdate;
@@ -6345,6 +6348,10 @@ void *miner_thread(void *userdata)
 
 	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 
+	char threadname[20];
+	snprintf(threadname, 20, "miner_%s%d.%d", api->name, cgpu->device_id, mythr->device_thread);
+	RenameThread(threadname);
+
 	gettimeofday(&getwork_start, NULL);
 
 	if (api->thread_init && !api->thread_init(mythr)) {
@@ -6525,6 +6532,8 @@ static void convert_to_work(json_t *val, int rolltime, struct pool *pool, struct
 		free_work(work);
 		return;
 	}
+	total_getworks++;
+	pool->getwork_requested++;
 	work->pool = pool;
 	memcpy(&(work->tv_getwork), tv_lp, sizeof(struct timeval));
 	memcpy(&(work->tv_getwork_reply), tv_lp_reply, sizeof(struct timeval));
@@ -6584,7 +6593,7 @@ static struct pool *select_longpoll_pool(struct pool *cp)
  */
 static void wait_lpcurrent(struct pool *pool)
 {
-	if (pool->enabled == POOL_REJECTING || pool_strategy == POOL_LOADBALANCE || pool_strategy == POOL_BALANCE)
+	if (cnx_needed(pool))
 		return;
 
 	while (pool != current_pool() && pool_strategy != POOL_LOADBALANCE && pool_strategy != POOL_BALANCE) {
@@ -6613,7 +6622,7 @@ static void *longpoll_thread(void *userdata)
 	char *lp_url;
 	int rolltime;
 
-	rename_thr("bfg-longpoll");
+	RenameThread("longpoll");
 
 	curl = curl_easy_init();
 	if (unlikely(!curl)) {
@@ -6797,7 +6806,7 @@ static void *watchpool_thread(void __maybe_unused *userdata)
 {
 	int intervals = 0;
 
-	rename_thr("bfg-watchpool");
+	RenameThread("watchpool");
 
 	while (42) {
 		struct timeval now;
@@ -6871,7 +6880,7 @@ static void *watchdog_thread(void __maybe_unused *userdata)
 	const unsigned int interval = WATCHDOG_INTERVAL;
 	struct timeval zero_tv;
 
-	rename_thr("bfg-watchdog");
+	RenameThread("watchdog");
 
 	memset(&zero_tv, 0, sizeof(struct timeval));
 	gettimeofday(&rotate_tv, NULL);
@@ -7513,8 +7522,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);
@@ -7607,18 +7623,6 @@ int main(int argc, char *argv[])
 	if (!config_loaded)
 		load_default_config();
 
-	if (opt_restart) {
-		struct sigaction shandler;
-
-		shandler.sa_handler = &seghandler;
-		shandler.sa_flags = 0;
-		sigemptyset(&shandler.sa_mask);
-		sigaction(SIGSEGV, &shandler, &segvhandler);
-		sigaction(SIGILL, &shandler, &illhandler);
-#ifndef WIN32
-		sigaction(SIGBUS, &shandler, &bushandler);
-#endif
-	}
 	if (opt_benchmark) {
 		struct pool *pool;
 

+ 10 - 2
miner.h

@@ -551,7 +551,6 @@ extern void thr_info_cancel(struct thr_info *thr);
 extern void thr_info_freeze(struct thr_info *thr);
 extern void nmsleep(unsigned int msecs);
 extern double us_tdiff(struct timeval *end, struct timeval *start);
-extern void rename_thr(const char* name);
 extern double tdiff(struct timeval *end, struct timeval *start);
 
 struct string_elist {
@@ -711,6 +710,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
@@ -740,6 +742,11 @@ 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 hash_lock;
 extern pthread_mutex_t console_lock;
 extern pthread_mutex_t ch_lock;
 
@@ -825,6 +832,7 @@ extern int opt_fail_pause;
 extern int opt_log_interval;
 extern unsigned long long global_hashrate;
 extern char *current_fullhash;
+extern uint64_t best_diff;
 extern struct timeval block_timeval;
 
 #ifdef HAVE_OPENCL
@@ -1008,7 +1016,7 @@ struct work {
 	unsigned char	target[32];
 	unsigned char	hash[32];
 
-	uint32_t	outputhash;
+	uint64_t	outputhash;
 
 	int		rolls;
 

+ 15 - 12
scrypt.c

@@ -250,11 +250,10 @@ PBKDF2_SHA256_80_128(const uint32_t * passwd, uint32_t * buf)
 }
 
 
-static inline uint32_t
-PBKDF2_SHA256_80_128_32(const uint32_t * passwd, const uint32_t * salt)
+static inline void
+PBKDF2_SHA256_80_128_32(const uint32_t * passwd, const uint32_t * salt, uint32_t *ostate)
 {
 	uint32_t tstate[8];
-	uint32_t ostate[8];
 	uint32_t ihash[8];
 	uint32_t i;
 
@@ -292,8 +291,6 @@ PBKDF2_SHA256_80_128_32(const uint32_t * passwd, const uint32_t * salt)
 
 	/* Feed the inner hash to the outer SHA256 operation. */
 	SHA256_Transform(ostate, pad, 0);
-	/* Finish the outer SHA256 operation. */
-	return be32toh(ostate[7]);
 }
 
 
@@ -359,7 +356,7 @@ salsa20_8(uint32_t B[16], const uint32_t Bx[16])
 /* cpu and memory intensive function to transform a 80 byte buffer into a 32 byte output
    scratchpad size needs to be at least 63 + (128 * r * p) + (256 * r + 64) + (128 * r * N) bytes
  */
-static uint32_t scrypt_1024_1_1_256_sp(const uint32_t* input, char* scratchpad)
+static void scrypt_1024_1_1_256_sp(const uint32_t* input, char* scratchpad, uint32_t *ostate)
 {
 	uint32_t * V;
 	uint32_t X[32];
@@ -402,32 +399,35 @@ static uint32_t scrypt_1024_1_1_256_sp(const uint32_t* input, char* scratchpad)
 		salsa20_8(&X[16], &X[0]);
 	}
 
-	return PBKDF2_SHA256_80_128_32(input, X);
+	PBKDF2_SHA256_80_128_32(input, X, ostate);
 }
 
 void scrypt_outputhash(struct work *work)
 {
-	uint32_t data[20];
+	uint32_t data[20], ohash[8], rhash[8];
 	char *scratchbuf;
 	uint32_t *nonce = (uint32_t *)(work->data + 76);
 
 	be32enc_vect(data, (const uint32_t *)work->data, 19);
 	data[19] = htobe32(*nonce);
 	scratchbuf = alloca(131584);
-	work->outputhash = scrypt_1024_1_1_256_sp(data, scratchbuf);
+	scrypt_1024_1_1_256_sp(data, scratchbuf, ohash);
+	swap256(rhash, ohash);
+	work->outputhash = be64toh(*((uint64_t *)rhash));
 }
 
 /* Used externally as confirmation of correct OCL code */
 bool scrypt_test(unsigned char *pdata, const unsigned char *ptarget, uint32_t nonce)
 {
 	uint32_t tmp_hash7, Htarg = ((const uint32_t *)ptarget)[7];
+	uint32_t data[20], ohash[8];
 	char *scratchbuf;
-	uint32_t data[20];
 
 	be32enc_vect(data, (const uint32_t *)pdata, 19);
 	data[19] = htobe32(nonce);
 	scratchbuf = alloca(131584);
-	tmp_hash7 = scrypt_1024_1_1_256_sp(data, scratchbuf);
+	scrypt_1024_1_1_256_sp(data, scratchbuf, ohash);
+	tmp_hash7 = be32toh(ohash[7]);
 
 	return (tmp_hash7 <= Htarg);
 }
@@ -453,9 +453,12 @@ bool scanhash_scrypt(struct thr_info *thr, const unsigned char __maybe_unused *p
 	}
 
 	while(1) {
+		uint32_t ostate[8];
+
 		*nonce = ++n;
 		data[19] = n;
-		tmp_hash7 = scrypt_1024_1_1_256_sp(data, scratchbuf);
+		scrypt_1024_1_1_256_sp(data, scratchbuf, ostate);
+		tmp_hash7 = be32toh(ostate[7]);
 
 		if (unlikely(tmp_hash7 <= Htarg)) {
 			((uint32_t *)pdata)[19] = htobe32(n);

+ 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

+ 55 - 27
util.c

@@ -789,20 +789,6 @@ double us_tdiff(struct timeval *end, struct timeval *start)
 	return end->tv_sec * 1000000 + end->tv_usec - start->tv_sec * 1000000 - start->tv_usec;
 }
 
-void rename_thr(const char* name) {
-#if defined(PR_SET_NAME)
-	// Only the first 15 characters are used (16 - NUL terminator)
-	prctl(PR_SET_NAME, name, 0, 0, 0);
-#elif defined(__APPLE__)
-	pthread_setname_np(name);
-#elif defined(__FreeBSD__) || defined(__OpenBSD__)
-	pthread_set_name_np(pthread_self(), name);
-#else
-	// Prevent warnings for unused parameters...
-	(void)name;
-#endif
-}
-
 /* Returns the seconds difference between end and start times as a double */
 double tdiff(struct timeval *end, struct timeval *start)
 {
@@ -1420,45 +1406,87 @@ out:
 	return ret;
 }
 
+void suspend_stratum(struct pool *pool)
+{
+	applog(LOG_INFO, "Closing socket for stratum pool %d", pool->pool_no);
+	mutex_lock(&pool->stratum_lock);
+	pool->stratum_active = false;
+	mutex_unlock(&pool->stratum_lock);
+	CLOSESOCKET(pool->sock);
+}
 
 void dev_error(struct cgpu_info *dev, enum dev_reason reason)
 {
 	dev->device_last_not_well = time(NULL);
 	dev->device_not_well_reason = reason;
 
-
-	switch (reason)
-	{
+	switch (reason) {
 		case REASON_THREAD_FAIL_INIT:
 			dev->thread_fail_init_count++;
-		break;
+			break;
 		case REASON_THREAD_ZERO_HASH:
 			dev->thread_zero_hash_count++;
-		break;
+			break;
 		case REASON_THREAD_FAIL_QUEUE:
 			dev->thread_fail_queue_count++;
-		break;
+			break;
 		case REASON_DEV_SICK_IDLE_60:
 			dev->dev_sick_idle_60_count++;
-		break;
+			break;
 		case REASON_DEV_DEAD_IDLE_600:
 			dev->dev_dead_idle_600_count++;
-		break;
+			break;
 		case REASON_DEV_NOSTART:
 			dev->dev_nostart_count++;
-		break;
+			break;
 		case REASON_DEV_OVER_HEAT:
 			dev->dev_over_heat_count++;
-		break;
+			break;
 		case REASON_DEV_THERMAL_CUTOFF:
 			dev->dev_thermal_cutoff_count++;
-		break;
+			break;
 		case REASON_DEV_COMMS_ERROR:
 			dev->dev_comms_error_count++;
-		break;
+			break;
 		case REASON_DEV_THROTTLE:
 			dev->dev_throttle_count++;
-		break;
+			break;
 	}
+}
+
+/* Realloc an existing string to fit an extra string s, appending s to it. */
+void *realloc_strcat(char *ptr, char *s)
+{
+	size_t old = strlen(ptr), len = strlen(s);
+	char *ret;
+
+	if (!len)
+		return ptr;
+
+	len += old + 1;
+	if (len % 4)
+		len += 4 - (len % 4);
+
+	ret = malloc(len);
+	if (unlikely(!ret))
+		quit(1, "Failed to malloc in realloc_strcat");
+
+	sprintf(ret, "%s%s", ptr, s);
+	free(ptr);
+	return ret;
+}
 
+void RenameThread(const char* name)
+{
+#if defined(PR_SET_NAME)
+	// Only the first 15 characters are used (16 - NUL terminator)
+	prctl(PR_SET_NAME, name, 0, 0, 0);
+#elif defined(__APPLE__)
+	pthread_setname_np(name);
+#elif (defined(__FreeBSD__) || defined(__OpenBSD__))
+	pthread_set_name_np(pthread_self(), name);
+#else
+	// Prevent warnings for unused parameters...
+	(void)name;
+#endif
 }

+ 3 - 0
util.h

@@ -51,6 +51,9 @@ bool parse_method(struct pool *pool, char *s);
 bool extract_sockaddr(struct pool *pool, char *url);
 bool auth_stratum(struct pool *pool);
 bool initiate_stratum(struct pool *pool);
+void suspend_stratum(struct pool *pool);
 void dev_error(struct cgpu_info *dev, enum dev_reason reason);
+void *realloc_strcat(char *ptr, char *s);
+void RenameThread(const char* name);
 
 #endif /* __UTIL_H__ */

+ 34 - 2
windows-build.txt

@@ -18,7 +18,7 @@ If you think that this documentation was helpful and you wish to donate, you can
 do so at the following address. 12KaKtrK52iQjPdtsJq7fJ7smC32tXWbWr
 
 **************************************************************************************
-* A tip that might help you along the way                                             *
+* A tip that might help you along the way                                            *
 **************************************************************************************
 Enable "QuickEdit Mode" in your Command Prompt Window or MinGW Command Prompt
 Window (No need to go into the context menu to choose edit-mark/copy/paste):
@@ -137,6 +137,7 @@ to the next.
       autoreconf -fvi
       CFLAGS="-O2 -msse2" ./configure (additional config options, see below)
       make
+      strip bfgminer.exe  <== only do this if you are not compiling for debugging
 
 **************************************************************************************
 * Copy files to a build directory/folder                                             *
@@ -177,10 +178,41 @@ Now you can get the latest source directly from github.
 * Optional - Install libusb if you need auto USB device detection                    *
 * Required for Ztex and X6500                                                        *
 **************************************************************************************
+Go to this url ==> http://git.libusb.org/?p=libusb.git;a=snapshot;h=master;sf=zip
+save the file to your local storage. Open the file and copy the libusb* folder to
+\MinGW\msys\1.0\home\(your user directory/folder).
+Or if you do not want to download the file directly and would like to use git then
+Type the following from the MSYS shell in your home folder.
+git clone git://git.libusb.org/libusb.git
+
+Run the MinGW MSYS shell
+(Start Icon/keyboard key ==> All Programs ==> MinGW ==> MinGW Shell).
+Change the working directory to your libusb project folder.
+Example: cd libusb-something [Enter Key] if you are unsure then type "ls -la"
+Another way is to type "cd libusb" and then press the tab key; It will auto fill.
+Type the lines below one at a time. Look for problems after each one before going on
+to the next.
+
+./autogen.sh --disable-debug-log
+./configure --prefix=/MinGW
+make
+make install
+
+You may now exit the MSYS shell.
+Ctrl-D or typing "logout" and pressing the enter key should get you out of the
+window.
+
+You will have to copy "libusb-1.0.dll" to your working BFGMiner binary directory.
+You will find "libusb-1.0.dll" in the \MinGW\bin directory/folder.
+
+Use this method if libusb does not work for you on Ztex. Once someone lets us know
+Libusb works instead of libusbx then we will remove the section below this line.
+Run the MSYS shell and change into the libusb folder as above.
+Type ==> make uninstall
 Go to this url ==> http://libusbx.org/
 Click on the "Downloads" tab.
 Click on "releases".
-Click on the latest version. I downloaded 1.0.12; yours may be newer.
+Click on the latest version. I downloaded 1.0.14; yours may be newer.
 Do not download from the link that says "Looking for the latest version?".
 Click on "Windows"
 Click on the file and download it. I downloaded libusbx-1.0.12-win.7z.