Browse Source

Merge branch 'cg_merges_20131108a' into bfgminer

Conflicts:
	miner.c
Luke Dashjr 12 years ago
parent
commit
56e99a7a72
13 changed files with 1918 additions and 184 deletions
  1. 5 6
      Makefile.am
  2. 21 20
      README
  3. 8 2
      README.ASIC
  4. 7 0
      README.RPC
  5. 28 1
      api.c
  6. 26 18
      configure.ac
  7. 1654 0
      driver-klondike.c
  8. 2 2
      logging.h
  9. 119 120
      miner.c
  10. 34 5
      miner.h
  11. 0 10
      sha2.c
  12. 12 0
      sha2.h
  13. 2 0
      util.h

+ 5 - 6
Makefile.am

@@ -105,13 +105,8 @@ bfgminer_CPPFLAGS += $(libevent_CFLAGS)
 endif
 endif
 
 
 
 
-# GPU sources, TODO: make them selectable
-# the GPU portion extracted from original main.c
-bfgminer_SOURCES += driver-opencl.h driver-opencl.c
-
 if HAVE_OPENCL
 if HAVE_OPENCL
-
-# the original GPU related sources, unchanged
+bfgminer_SOURCES += driver-opencl.h driver-opencl.c
 bfgminer_SOURCES += ocl.c ocl.h findnonce.c findnonce.h
 bfgminer_SOURCES += ocl.c ocl.h findnonce.c findnonce.h
 bfgminer_SOURCES += adl.c adl.h adl_functions.h
 bfgminer_SOURCES += adl.c adl.h adl_functions.h
 bfgminer_SOURCES += *.cl
 bfgminer_SOURCES += *.cl
@@ -228,6 +223,10 @@ if USE_KNC
 bfgminer_SOURCES += driver-knc.c
 bfgminer_SOURCES += driver-knc.c
 endif
 endif
 
 
+if HAS_KLONDIKE
+bfgminer_SOURCES += driver-klondike.c
+endif
+
 if HAS_MODMINER
 if HAS_MODMINER
 bfgminer_SOURCES += driver-modminer.c
 bfgminer_SOURCES += driver-modminer.c
 endif
 endif

+ 21 - 20
README

@@ -38,15 +38,6 @@ MORE INFORMATION ON EACH.
 
 
 EXECUTIVE SUMMARY ON USAGE:
 EXECUTIVE SUMMARY ON USAGE:
 
 
-After saving configuration from the menu, you do not need to give BFGMiner any
-arguments and it will load your configuration.
-
-Any configuration file may also contain a single
-	"include" : "filename"
-to recursively include another configuration file.
-Writing the configuration will save all settings from all files in the output.
-
-
 Single pool:
 Single pool:
 
 
 bfgminer -o http://pool:port -u username -p password
 bfgminer -o http://pool:port -u username -p password
@@ -55,11 +46,11 @@ Multiple pools:
 
 
 bfgminer -o http://pool1:port -u pool1username -p pool1password -o http://pool2:port -u pool2usernmae -p pool2password
 bfgminer -o http://pool1:port -u pool1username -p pool1password -o http://pool2:port -u pool2usernmae -p pool2password
 
 
-Single pool with a standard http proxy, regular desktop:
+Single pool with a standard http proxy:
 
 
 bfgminer -o http://pool:port -x http://proxy:port -u username -p password
 bfgminer -o http://pool:port -x http://proxy:port -u username -p password
 
 
-Single pool with a socks5 proxy, regular desktop:
+Single pool with a socks5 proxy:
 
 
 bfgminer -o http://pool:port -x socks5://proxy:port -u username -p password
 bfgminer -o http://pool:port -x socks5://proxy:port -u username -p password
 
 
@@ -75,6 +66,16 @@ Proxy support requires cURL version 7.21.7 or newer.
 If you specify the --socks-proxy option to BFGMiner, it will only be applied to
 If you specify the --socks-proxy option to BFGMiner, it will only be applied to
 all pools that don't specify their own proxy setting like above
 all pools that don't specify their own proxy setting like above
 
 
+
+After saving configuration from the menu, you do not need to give BFGMiner any
+arguments and it will load your configuration.
+
+Any configuration file may also contain a single
+	"include" : "filename"
+to recursively include another configuration file.
+Writing the configuration will save all settings from all files in the output.
+
+
 ---
 ---
 BUILDING BFGMINER
 BUILDING BFGMINER
 
 
@@ -115,7 +116,7 @@ Optional Dependencies:
 	Stratum proxy:
 	Stratum proxy:
 	  libevent 2.0.3+    http://libevent.org/
 	  libevent 2.0.3+    http://libevent.org/
 
 
-	X6500 and ZTEX FPGA boards:
+	Klondike, X6500 and ZTEX FPGA boards:
 	  libusb-1.0-0-dev   http://www.libusb.org/
 	  libusb-1.0-0-dev   http://www.libusb.org/
 
 
 	Video card GPU mining (free):
 	Video card GPU mining (free):
@@ -145,6 +146,7 @@ BFGMiner specific configuration options:
 	--enable-metabank       Compile support for Metabank (default disabled)
 	--enable-metabank       Compile support for Metabank (default disabled)
 	--disable-bitforce      Compile support for BitForce (default enabled)
 	--disable-bitforce      Compile support for BitForce (default enabled)
 	--disable-icarus        Compile support for Icarus (default enabled)
 	--disable-icarus        Compile support for Icarus (default enabled)
+	--enable-klondike       Compile support for Klondike (default disabled)
 	--enable-knc            Compile support for KnC (default disabled)
 	--enable-knc            Compile support for KnC (default disabled)
 	--disable-modminer      Compile support for ModMiner (default enabled)
 	--disable-modminer      Compile support for ModMiner (default enabled)
 	--disable-x6500         Compile support for X6500 (default enabled)
 	--disable-x6500         Compile support for X6500 (default enabled)
@@ -182,7 +184,8 @@ path like this (if you are in the bfgminer directory already): ./bfgminer
 
 
 Usage instructions:  Run "bfgminer --help" to see options:
 Usage instructions:  Run "bfgminer --help" to see options:
 
 
-Usage: . [-atDdGCgIKklmpPQqrRsTouvwOchnV] 
+Usage: bfgminer [-DdElmpPQqUsTouOchnV]
+
 Options for both config file and command line:
 Options for both config file and command line:
 --api-allow         Allow API access (if enabled) only to the given list of [W:]IP[/Prefix] address[/subnets]
 --api-allow         Allow API access (if enabled) only to the given list of [W:]IP[/Prefix] address[/subnets]
                     This overrides --api-network and you must specify 127.0.0.1 if it is required
                     This overrides --api-network and you must specify 127.0.0.1 if it is required
@@ -698,7 +701,7 @@ Q: Work keeps going to my backup pool even though my primary pool hasn't
 failed?
 failed?
 A: BFGMiner checks for conditions where the primary pool is lagging and will
 A: BFGMiner checks for conditions where the primary pool is lagging and will
 pass some work to the backup servers under those conditions. The reason for
 pass some work to the backup servers under those conditions. The reason for
-doing this is to try its absolute best to keep the GPUs working on something
+doing this is to try its absolute best to keep the devices working on something
 useful and not risk idle periods. You can disable this behaviour with the
 useful and not risk idle periods. You can disable this behaviour with the
 option --failover-only.
 option --failover-only.
 
 
@@ -725,8 +728,7 @@ other factors.
 
 
 Q: What are the best parameters to pass for X pool/hardware/device.
 Q: What are the best parameters to pass for X pool/hardware/device.
 A: Virtually always, the DEFAULT parameters give the best results. Most user
 A: Virtually always, the DEFAULT parameters give the best results. Most user
-defined settings lead to worse performance. The ONLY thing most users should
-need to set is the Intensity for GPUs.
+defined settings lead to worse performance.
 
 
 Q: What happened to CPU mining?
 Q: What happened to CPU mining?
 A: Being increasingly irrelevant for most users, and a maintenance issue, it is
 A: Being increasingly irrelevant for most users, and a maintenance issue, it is
@@ -754,7 +756,7 @@ of thermal damage. It is highly recommended not to mine on a Mac unless it is to
 a USB device.
 a USB device.
 
 
 Q: My network gets slower and slower and then dies for a minute?
 Q: My network gets slower and slower and then dies for a minute?
-A; Try the --net-delay option.
+A; Try the --net-delay option if you are on a getwork or GBT server.
 
 
 Q: How do I tune for P2Pool?
 Q: How do I tune for P2Pool?
 A: P2Pool has very rapid expiration of work and new blocks, it is suggested you
 A: P2Pool has very rapid expiration of work and new blocks, it is suggested you
@@ -781,9 +783,8 @@ been skipped. "PGA" is also used for devices built with Application-Specific
 Integrated Circuits (ASICs).
 Integrated Circuits (ASICs).
 
 
 Q: What is an ASIC?
 Q: What is an ASIC?
-A: BFGMiner currently supports 2 ASICs: Avalon and BitForce SC devices. They are
-Application Specify Integrated Circuit devices and provide the highest
-performance per unit power due to being dedicated to only one purpose.
+A: They are Application Specify Integrated Circuit devices and provide the
+highest performance per unit power due to being dedicated to only one purpose.
 
 
 Q: How do I get my BFL/Icarus/Lancelot/Cairnsmore device to auto-recognise?
 Q: How do I get my BFL/Icarus/Lancelot/Cairnsmore device to auto-recognise?
 A: On Linux, if the /dev/ttyUSB* devices don't automatically appear, the only
 A: On Linux, if the /dev/ttyUSB* devices don't automatically appear, the only

+ 8 - 2
README.ASIC

@@ -1,8 +1,8 @@
 SUPPORTED DEVICES
 SUPPORTED DEVICES
 
 
 Currently supported ASIC devices include Avalon, Bitfountain's Block Erupter
 Currently supported ASIC devices include Avalon, Bitfountain's Block Erupter
-series (both USB and blades), a large variety of Bitfury-based miners, and
-Butterfly Labs' SC range of devices.
+series (both USB and blades), a large variety of Bitfury-based miners, Butterfly
+Labs' SC range of devices, and Klondike modules.
 
 
 
 
 AVALON
 AVALON
@@ -121,6 +121,12 @@ ports. They communicate with the Icarus protocol, which has some additional
 options in README.FPGA
 options in README.FPGA
 
 
 
 
+KLONDIKE
+--------
+
+--klondike-options <arg> Set klondike options clock:temptarget
+
+
 ---
 ---
 
 
 This code is provided entirely free of charge by the programmer in his spare
 This code is provided entirely free of charge by the programmer in his spare

+ 7 - 0
README.RPC

@@ -470,6 +470,13 @@ api-example.py - a Python script to access the API
 Feature Changelog for external applications using the API:
 Feature Changelog for external applications using the API:
 
 
 
 
+API V2.2
+
+Modified API command:
+ 'pools' - add 'Works'
+
+---------
+
 API V2.1 (BFGMiner v3.4.0)
 API V2.1 (BFGMiner v3.4.0)
 
 
 Added API command:
 Added API command:

+ 28 - 1
api.c

@@ -60,7 +60,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 static const char GPUSEP = ',';
 
 
-static const char *APIVERSION = "2.1";
+static const char *APIVERSION = "2.2";
 static const char *DEAD = "Dead";
 static const char *DEAD = "Dead";
 static const char *SICK = "Sick";
 static const char *SICK = "Sick";
 static const char *NOSTART = "NoStart";
 static const char *NOSTART = "NoStart";
@@ -771,6 +771,16 @@ static struct api_data *api_add_data_full(struct api_data *root, char *name, enu
 				api_data->data = (void *)malloc(strlen((char *)data) + 1);
 				api_data->data = (void *)malloc(strlen((char *)data) + 1);
 				strcpy((char*)(api_data->data), (char *)data);
 				strcpy((char*)(api_data->data), (char *)data);
 				break;
 				break;
+			case API_UINT8:
+				/* Most OSs won't really alloc less than 4 */
+				api_data->data = malloc(4);
+				*(uint8_t *)api_data->data = *(uint8_t *)data;
+				break;
+			case API_UINT16:
+				/* Most OSs won't really alloc less than 4 */
+				api_data->data = malloc(4);
+				*(uint16_t *)api_data->data = *(uint16_t *)data;
+				break;
 			case API_INT:
 			case API_INT:
 				api_data->data = (void *)malloc(sizeof(int));
 				api_data->data = (void *)malloc(sizeof(int));
 				*((int *)(api_data->data)) = *((int *)data);
 				*((int *)(api_data->data)) = *((int *)data);
@@ -846,6 +856,16 @@ struct api_data *api_add_const(struct api_data *root, char *name, const char *da
 	return api_add_data_full(root, name, API_CONST, (void *)data, copy_data);
 	return api_add_data_full(root, name, API_CONST, (void *)data, copy_data);
 }
 }
 
 
+struct api_data *api_add_uint8(struct api_data *root, char *name, uint8_t *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_UINT8, (void *)data, copy_data);
+}
+
+struct api_data *api_add_uint16(struct api_data *root, char *name, uint16_t *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_UINT16, (void *)data, copy_data);
+}
+
 struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool copy_data)
 struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool copy_data)
 {
 {
 	return api_add_data_full(root, name, API_INT, (void *)data, copy_data);
 	return api_add_data_full(root, name, API_INT, (void *)data, copy_data);
@@ -984,6 +1004,12 @@ static struct api_data *print_data(struct api_data *root, char *buf, bool isjson
 				if (escape != original)
 				if (escape != original)
 					free(escape);
 					free(escape);
 				break;
 				break;
+			case API_UINT8:
+				sprintf(buf, "%u", *(uint8_t *)root->data);
+				break;
+			case API_UINT16:
+				sprintf(buf, "%u", *(uint16_t *)root->data);
+				break;
 			case API_INT:
 			case API_INT:
 				sprintf(buf, "%d", *((int *)(root->data)));
 				sprintf(buf, "%d", *((int *)(root->data)));
 				break;
 				break;
@@ -1965,6 +1991,7 @@ static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m
 		root = api_add_uint(root, "Getworks", &(pool->getwork_requested), false);
 		root = api_add_uint(root, "Getworks", &(pool->getwork_requested), false);
 		root = api_add_int(root, "Accepted", &(pool->accepted), false);
 		root = api_add_int(root, "Accepted", &(pool->accepted), false);
 		root = api_add_int(root, "Rejected", &(pool->rejected), false);
 		root = api_add_int(root, "Rejected", &(pool->rejected), false);
+		root = api_add_int(root, "Works", &pool->works, false);
 		root = api_add_uint(root, "Discarded", &(pool->discarded_work), false);
 		root = api_add_uint(root, "Discarded", &(pool->discarded_work), false);
 		root = api_add_uint(root, "Stale", &(pool->stale_shares), false);
 		root = api_add_uint(root, "Stale", &(pool->stale_shares), false);
 		root = api_add_uint(root, "Get Failures", &(pool->getfail_occasions), false);
 		root = api_add_uint(root, "Get Failures", &(pool->getfail_occasions), false);

+ 26 - 18
configure.ac

@@ -215,10 +215,10 @@ fi
 AM_CONDITIONAL([HAS_CPUMINE], [test x$cpumining = xyes])
 AM_CONDITIONAL([HAS_CPUMINE], [test x$cpumining = xyes])
 
 
 driverlist="$driverlist opencl"
 driverlist="$driverlist opencl"
-opencl="yes"
+opencl="no"
 
 
 AC_ARG_ENABLE([opencl],
 AC_ARG_ENABLE([opencl],
-	[AC_HELP_STRING([--disable-opencl],[Build without support for OpenCL (default enabled)])],
+	[AC_HELP_STRING([--enable-opencl],[Compile support for OpenCL (default disabled)])],
 	[opencl=$enableval]
 	[opencl=$enableval]
 	)
 	)
 if test "x$opencl" = xyes; then
 if test "x$opencl" = xyes; then
@@ -384,6 +384,7 @@ if test "x$avalon" = xyes; then
 fi
 fi
 AM_CONDITIONAL([HAS_AVALON], [test x$avalon = xyes])
 AM_CONDITIONAL([HAS_AVALON], [test x$avalon = xyes])
 
 
+
 driverlist="$driverlist knc"
 driverlist="$driverlist knc"
 AC_ARG_ENABLE([knc],
 AC_ARG_ENABLE([knc],
 	[AC_HELP_STRING([--enable-knc],[Compile support for KnC (default disabled)])],
 	[AC_HELP_STRING([--enable-knc],[Compile support for KnC (default disabled)])],
@@ -510,6 +511,28 @@ PKG_CHECK_MODULES([LIBUSB], [libusb-1.0],[
 	fi
 	fi
 ])
 ])
 
 
+driverlist="$driverlist klondike"
+AC_ARG_ENABLE([klondike],
+	[AC_HELP_STRING([--enable-klondike],[Compile support for Klondike (default disabled)])],
+	[klondike=$enableval],
+	[klondike=auto]
+	)
+if test "x$klondike$libusb" = xyesno; then
+	AC_MSG_ERROR([Could not find libusb, required for Klondike support])
+elif test "x$klondike" = xauto; then
+	klondike="$libusb"
+	if test "x$libusb" = xno; then
+		AC_MSG_WARN([Could not find libusb, required for Klondike support])
+		klondike_enableaction="install libusb 1.0+"
+	fi
+fi
+if test "x$klondike" = xyes; then
+	AC_DEFINE([USE_KLONDIKE], [1], [Defined to 1 if Klondike support is wanted])
+	need_lowlevel=yes
+fi
+AM_CONDITIONAL([HAS_KLONDIKE], [test x$klondike = xyes])
+
+
 driverlist="$driverlist x6500"
 driverlist="$driverlist x6500"
 AC_ARG_ENABLE([x6500],
 AC_ARG_ENABLE([x6500],
 	[AC_HELP_STRING([--disable-x6500],[Compile support for X6500 (default if libusb)])],
 	[AC_HELP_STRING([--disable-x6500],[Compile support for X6500 (default if libusb)])],
@@ -719,7 +742,7 @@ fi
 AM_CONDITIONAL([HAS_METABANK], [test x$metabank = xyes])
 AM_CONDITIONAL([HAS_METABANK], [test x$metabank = xyes])
 
 
 
 
-if test "x$x6500$ztex" = "xnono"; then
+if test "x$klondike$x6500$ztex" = "xnonono"; then
 	libusb=no
 	libusb=no
 	LIBUSB_LIBS=''
 	LIBUSB_LIBS=''
 	LIBUSB_CFLAGS=''
 	LIBUSB_CFLAGS=''
@@ -1130,21 +1153,6 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([static int __attribute__((warn_unused_result)
 			  AC_DEFINE([HAVE_WARN_UNUSED_RESULT], [1],
 			  AC_DEFINE([HAVE_WARN_UNUSED_RESULT], [1],
                                     [Define if __attribute__((warn_unused_result))]))
                                     [Define if __attribute__((warn_unused_result))]))
 
 
-AC_MSG_CHECKING([for roundl function])
-save_LIBS="${LIBS}"
-LIBS="$LIBS -lm"
-AC_LINK_IFELSE([AC_LANG_PROGRAM([[
-	#include <math.h>
-]], [[
-	return (roundl(*(long double *)0xdeadbeef) == 1.0);
-]])], [
-	AC_MSG_RESULT([yes])
-], [
-	AC_MSG_RESULT([no])
-	AC_DEFINE([NEED_ROUNDL], [1], [Defined to 1 if C99 roundl is missing])
-])
-LIBS="${save_LIBS}"
-
 
 
 # byteswap functions
 # byteswap functions
 AH_TEMPLATE([HAVE_BYTESWAP_H], [Define to use byteswap macros from byteswap.h])
 AH_TEMPLATE([HAVE_BYTESWAP_H], [Define to use byteswap macros from byteswap.h])

+ 1654 - 0
driver-klondike.c

@@ -0,0 +1,1654 @@
+/*
+ * Copyright 2013 Andrew Smith
+ * Copyright 2013 Con Kolivas
+ * Copyright 2013 Chris Savery
+ *
+ * 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 <float.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "config.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include "compat.h"
+#include "deviceapi.h"
+#include "lowlevel.h"
+#include "miner.h"
+
+#define K1 "K1"
+#define K16 "K16"
+#define K64 "K64"
+
+static const char *msg_detect_send = "DSend";
+static const char *msg_detect_reply = "DReply";
+static const char *msg_send = "Send";
+static const char *msg_reply = "Reply";
+
+#define KLN_CMD_ABORT	'A'
+#define KLN_CMD_CONFIG	'C'
+#define KLN_CMD_ENABLE	'E'
+#define KLN_CMD_IDENT	'I'
+#define KLN_CMD_NONCE	'='
+#define KLN_CMD_STATUS	'S'
+#define KLN_CMD_WORK	'W'
+
+#define KLN_CMD_ENABLE_OFF	'0'
+#define KLN_CMD_ENABLE_ON	'1'
+
+#define MIDSTATE_BYTES 32
+#define MERKLE_OFFSET 64
+#define MERKLE_BYTES 12
+
+#define REPLY_SIZE		15	// adequate for all types of replies
+#define MAX_KLINES		1024	// unhandled reply limit
+#define REPLY_WAIT_TIME		100 	// poll interval for a cmd waiting it's reply
+#define CMD_REPLY_RETRIES	8	// how many retries for cmds
+#define MAX_WORK_COUNT		4	// for now, must be binary multiple and match firmware
+#define TACH_FACTOR		87890	// fan rpm divisor
+
+#define KLN_KILLWORK_TEMP	53.5
+#define KLN_COOLED_DOWN		45.5
+
+/*
+ *  Work older than 5s will already be completed
+ *  FYI it must not be possible to complete 256 work
+ *  items this quickly on a single device -
+ *  thus limited to 219.9GH/s per device
+ */
+#define OLD_WORK_MS ((int)(5 * 1000))
+
+/*
+ * How many incorrect slave counts to ignore in a row
+ * 2 means it allows random grabage returned twice
+ * Until slaves are implemented, this should never occur
+ * so allowing 2 in a row should ignore random errros
+ */
+#define KLN_ISS_IGNORE 2
+
+/*
+ * If the queue status hasn't been updated for this long then do it now
+ * 5GH/s = 859ms per full nonce range
+ */
+#define LATE_UPDATE_MS ((int)(2.5 * 1000))
+
+// If 5 late updates in a row, try to reset the device
+#define LATE_UPDATE_LIMIT	5
+
+// If the reset fails sleep for 1s
+#define LATE_UPDATE_SLEEP_MS 1000
+
+// However give up after 8s
+#define LATE_UPDATE_NODEV_MS ((int)(8.0 * 1000))
+
+BFG_REGISTER_DRIVER(klondike_drv)
+
+typedef struct klondike_header {
+	uint8_t cmd;
+	uint8_t dev;
+	uint8_t buf[REPLY_SIZE-2];
+} HEADER;
+
+#define K_2(_bytes) ((int)(_bytes[0]) + \
+			((int)(_bytes[1]) << 8))
+
+#define K_4(_bytes) ((uint64_t)(_bytes[0]) + \
+			((uint64_t)(_bytes[1]) << 8) + \
+			((uint64_t)(_bytes[2]) << 16) + \
+			((uint64_t)(_bytes[3]) << 24))
+
+#define K_SERIAL(_serial) K_4(_serial)
+#define K_HASHCOUNT(_hashcount) K_2(_hashcount)
+#define K_MAXCOUNT(_maxcount) K_2(_maxcount)
+#define K_NONCE(_nonce) K_4(_nonce)
+#define K_HASHCLOCK(_hashclock) K_2(_hashclock)
+
+#define SET_HASHCLOCK(_hashclock, _value) do { \
+						(_hashclock)[0] = (uint8_t)((_value) & 0xff); \
+						(_hashclock)[1] = (uint8_t)(((_value) >> 8) & 0xff); \
+					  } while(0)
+
+#define KSENDHD(_add) (sizeof(uint8_t) + sizeof(uint8_t) + _add)
+
+typedef struct klondike_id {
+	uint8_t cmd;
+	uint8_t dev;
+	uint8_t version;
+	uint8_t product[7];
+	uint8_t serial[4];
+} IDENTITY;
+
+typedef struct klondike_status {
+	uint8_t cmd;
+	uint8_t dev;
+	uint8_t state;
+	uint8_t chipcount;
+	uint8_t slavecount;
+	uint8_t workqc;
+	uint8_t workid;
+	uint8_t temp;
+	uint8_t fanspeed;
+	uint8_t errorcount;
+	uint8_t hashcount[2];
+	uint8_t maxcount[2];
+	uint8_t noise;
+} WORKSTATUS;
+
+typedef struct _worktask {
+	uint8_t cmd;
+	uint8_t dev;
+	uint8_t workid;
+	uint8_t midstate[32];
+	uint8_t merkle[12];
+} WORKTASK;
+
+typedef struct _workresult {
+	uint8_t cmd;
+	uint8_t dev;
+	uint8_t workid;
+	uint8_t nonce[4];
+} WORKRESULT;
+
+typedef struct klondike_cfg {
+	uint8_t cmd;
+	uint8_t dev;
+	uint8_t hashclock[2];
+	uint8_t temptarget;
+	uint8_t tempcritical;
+	uint8_t fantarget;
+	uint8_t pad2;
+} WORKCFG;
+
+typedef struct kline {
+	union {
+		HEADER hd;
+		IDENTITY id;
+		WORKSTATUS ws;
+		WORKTASK wt;
+		WORKRESULT wr;
+		WORKCFG cfg;
+	};
+} KLINE;
+
+#define zero_kline(_kline) memset((void *)(_kline), 0, sizeof(KLINE));
+
+typedef struct device_info {
+	uint32_t noncecount;
+	uint32_t nextworkid;
+	uint16_t lasthashcount;
+	uint64_t totalhashcount;
+	uint32_t rangesize;
+	uint32_t *chipstats;
+} DEVINFO;
+
+typedef struct klist {
+	struct klist *prev;
+	struct klist *next;
+	KLINE kline;
+	struct timeval tv_when;
+	int block_seq;
+	bool ready;
+	bool working;
+} KLIST;
+
+typedef struct jobque {
+	int workqc;
+	struct timeval last_update;
+	bool overheat;
+	bool flushed;
+	int late_update_count;
+	int late_update_sequential;
+} JOBQUE;
+
+struct klondike_info {
+	pthread_rwlock_t stat_lock;
+	struct thr_info replies_thr;
+	cglock_t klist_lock;
+	KLIST *used;
+	KLIST *free;
+	int kline_count;
+	int used_count;
+	int block_seq;
+	KLIST *status;
+	DEVINFO *devinfo;
+	KLIST *cfg;
+	JOBQUE *jobque;
+	int noncecount;
+	uint64_t hashcount;
+	uint64_t errorcount;
+	uint64_t noisecount;
+	int incorrect_slave_sequential;
+
+	// us Delay from USB reply to being processed
+	double delay_count;
+	double delay_total;
+	double delay_min;
+	double delay_max;
+
+	struct timeval tv_last_nonce_received;
+
+	// Time from recieving one nonce to the next
+	double nonce_count;
+	double nonce_total;
+	double nonce_min;
+	double nonce_max;
+
+	int wque_size;
+	int wque_cleared;
+
+	bool initialised;
+	
+	struct libusb_device_handle *usbdev_handle;
+	
+	// TODO:
+	bool usbinfo_nodev;
+};
+
+static KLIST *new_klist_set(struct cgpu_info *klncgpu)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	KLIST *klist = NULL;
+	int i;
+
+	klist = calloc(MAX_KLINES, sizeof(*klist));
+	if (!klist)
+		quit(1, "Failed to calloc klist - when old count=%d", klninfo->kline_count);
+
+	klninfo->kline_count += MAX_KLINES;
+
+	klist[0].prev = NULL;
+	klist[0].next = &(klist[1]);
+	for (i = 1; i < MAX_KLINES-1; i++) {
+		klist[i].prev = &klist[i-1];
+		klist[i].next = &klist[i+1];
+	}
+	klist[MAX_KLINES-1].prev = &(klist[MAX_KLINES-2]);
+	klist[MAX_KLINES-1].next = NULL;
+
+	return klist;
+}
+
+static KLIST *allocate_kitem(struct cgpu_info *klncgpu)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	KLIST *kitem = NULL;
+	int ran_out = 0;
+	char errbuf[1024];
+
+	cg_wlock(&klninfo->klist_lock);
+
+	if (klninfo->free == NULL) {
+		ran_out = klninfo->kline_count;
+		klninfo->free = new_klist_set(klncgpu);
+		snprintf(errbuf, sizeof(errbuf),
+				 "%s%i: KLINE count exceeded %d, now %d",
+				 klncgpu->drv->name, klncgpu->device_id,
+				 ran_out, klninfo->kline_count);
+	}
+
+	kitem = klninfo->free;
+
+	klninfo->free = klninfo->free->next;
+	if (klninfo->free)
+		klninfo->free->prev = NULL;
+
+	kitem->next = klninfo->used;
+	kitem->prev = NULL;
+	if (kitem->next)
+		kitem->next->prev = kitem;
+	klninfo->used = kitem;
+
+	kitem->ready = false;
+	kitem->working = false;
+
+	memset((void *)&(kitem->kline), 0, sizeof(kitem->kline));
+
+	klninfo->used_count++;
+
+	cg_wunlock(&klninfo->klist_lock);
+
+	if (ran_out > 0)
+		applog(LOG_WARNING, "%s", errbuf);
+
+	return kitem;
+}
+
+static KLIST *release_kitem(struct cgpu_info *klncgpu, KLIST *kitem)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+
+	cg_wlock(&klninfo->klist_lock);
+
+	if (kitem == klninfo->used)
+		klninfo->used = kitem->next;
+
+	if (kitem->next)
+		kitem->next->prev = kitem->prev;
+	if (kitem->prev)
+		kitem->prev->next = kitem->next;
+
+	kitem->next = klninfo->free;
+	if (klninfo->free)
+		klninfo->free->prev = kitem;
+
+	kitem->prev = NULL;
+
+	klninfo->free = kitem;
+
+	klninfo->used_count--;
+
+	cg_wunlock(&klninfo->klist_lock);
+
+	return NULL;
+}
+
+static
+int usb_init(struct cgpu_info * const klncgpu, struct libusb_device * const dev)
+{
+	struct klondike_info * const klninfo = klncgpu->device_data;
+	int e;
+	if (libusb_open(dev, &klninfo->usbdev_handle) != LIBUSB_SUCCESS)
+		return 0;
+	if (LIBUSB_SUCCESS != (e = libusb_set_configuration(klninfo->usbdev_handle, 1)))
+	{
+		applog(LOG_DEBUG, "%s: Failed to set configuration 1: %s",
+		       klondike_drv.dname, bfg_strerror(e, BST_LIBUSB));
+fail:
+		libusb_close(klninfo->usbdev_handle);
+		return 0;
+	}
+	if (LIBUSB_SUCCESS != (e = libusb_claim_interface(klninfo->usbdev_handle, 0)))
+	{
+		applog(LOG_DEBUG, "%s: Failed to claim interface 0: %s",
+		       klondike_drv.dname, bfg_strerror(e, BST_LIBUSB));
+		goto fail;
+	}
+	return 1;
+}
+
+static
+int _usb_rw(struct cgpu_info * const klncgpu, void * const buf, const size_t bufsiz, int * const processed, int ep)
+{
+	struct klondike_info * const klninfo = klncgpu->device_data;
+	const unsigned int timeout = 999;
+	unsigned char *cbuf = buf;
+	int err, sent;
+	
+	*processed = 0;
+	
+	while (*processed < bufsiz)
+	{
+		err = libusb_bulk_transfer(klninfo->usbdev_handle, ep, cbuf, bufsiz, &sent, timeout);
+		if (unlikely(err))
+			return err;
+		*processed += sent;
+	}
+	
+	return LIBUSB_SUCCESS;
+}
+#define usb_read( klncgpu, buf, bufsiz, processed) _usb_rw(klncgpu, buf, bufsiz, processed, 1 | LIBUSB_ENDPOINT_IN)
+#define usb_write(klncgpu, buf, bufsiz, processed) _usb_rw(klncgpu, buf, bufsiz, processed, 1 | LIBUSB_ENDPOINT_OUT)
+
+static
+void usb_nodev(__maybe_unused struct cgpu_info * const klncgpu)
+{
+	// TODO
+}
+
+static
+void usb_uninit(struct cgpu_info * const klncgpu)
+{
+	struct klondike_info * const klninfo = klncgpu->device_data;
+	libusb_release_interface(klninfo->usbdev_handle, 0);
+	libusb_close(klninfo->usbdev_handle);
+}
+
+static double cvtKlnToC(uint8_t temp)
+{
+	double Rt, stein, celsius;
+
+	if (temp == 0)
+		return 0.0;
+
+	Rt = 1000.0 * 255.0 / (double)temp - 1000.0;
+
+	stein = log(Rt / 2200.0) / 3987.0;
+
+	stein += 1.0 / (double)(25.0 + 273.15);
+
+	celsius = (1.0 / stein) - 273.15;
+
+	// For display of bad data
+	if (celsius < 0.0)
+		celsius = 0.0;
+	if (celsius > 200.0)
+		celsius = 200.0;
+
+	return celsius;
+}
+
+static int cvtCToKln(double deg)
+{
+	double Rt, stein, temp;
+
+	if (deg < 0.0)
+		deg = 0.0;
+
+	stein = 1.0 / (deg + 273.15);
+
+	stein -= 1.0 / (double)(25.0 + 273.15);
+
+	Rt = exp(stein * 3987.0) * 2200.0;
+
+	if (Rt == -1000.0)
+		Rt++;
+
+	temp = 1000.0 * 256.0 / (Rt + 1000.0);
+
+	if (temp > 255)
+		temp = 255;
+	if (temp < 0)
+		temp = 0;
+
+	return (int)temp;
+}
+
+// Change this to LOG_WARNING if you wish to always see the replies
+#define READ_DEBUG LOG_DEBUG
+
+static void display_kline(struct cgpu_info *klncgpu, KLINE *kline, const char *msg)
+{
+	switch (kline->hd.cmd) {
+		case KLN_CMD_NONCE:
+			applog(READ_DEBUG,
+				"%s%i:%d %s work [%c] dev=%d workid=%d"
+				" nonce=0x%08x",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->wr.dev), msg, kline->wr.cmd,
+				(int)(kline->wr.dev),
+				(int)(kline->wr.workid),
+				(unsigned int)K_NONCE(kline->wr.nonce) - 0xC0);
+			break;
+		case KLN_CMD_STATUS:
+		case KLN_CMD_WORK:
+		case KLN_CMD_ENABLE:
+		case KLN_CMD_ABORT:
+			applog(READ_DEBUG,
+				"%s%i:%d %s status [%c] dev=%d chips=%d"
+				" slaves=%d workcq=%d workid=%d temp=%d fan=%d"
+				" errors=%d hashes=%d max=%d noise=%d",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->ws.dev), msg, kline->ws.cmd,
+				(int)(kline->ws.dev),
+				(int)(kline->ws.chipcount),
+				(int)(kline->ws.slavecount),
+				(int)(kline->ws.workqc),
+				(int)(kline->ws.workid),
+				(int)(kline->ws.temp),
+				(int)(kline->ws.fanspeed),
+				(int)(kline->ws.errorcount),
+				K_HASHCOUNT(kline->ws.hashcount),
+				K_MAXCOUNT(kline->ws.maxcount),
+				(int)(kline->ws.noise));
+			break;
+		case KLN_CMD_CONFIG:
+			applog(READ_DEBUG,
+				"%s%i:%d %s config [%c] dev=%d clock=%d"
+				" temptarget=%d tempcrit=%d fan=%d",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->cfg.dev), msg, kline->cfg.cmd,
+				(int)(kline->cfg.dev),
+				K_HASHCLOCK(kline->cfg.hashclock),
+				(int)(kline->cfg.temptarget),
+				(int)(kline->cfg.tempcritical),
+				(int)(kline->cfg.fantarget));
+			break;
+		case KLN_CMD_IDENT:
+			applog(READ_DEBUG,
+				"%s%i:%d %s info [%c] version=0x%02x prod=%.7s"
+				" serial=0x%08x",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->hd.dev), msg, kline->hd.cmd,
+				(int)(kline->id.version),
+				kline->id.product,
+				(unsigned int)K_SERIAL(kline->id.serial));
+			break;
+		default:
+		{
+			char hexdata[REPLY_SIZE * 2];
+			bin2hex(hexdata, &kline->hd.dev, REPLY_SIZE - 1);
+			applog(LOG_ERR,
+				"%s%i:%d %s [%c:%s] unknown and ignored",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->hd.dev), msg, kline->hd.cmd,
+				hexdata);
+			break;
+		}
+	}
+}
+
+static void display_send_kline(struct cgpu_info *klncgpu, KLINE *kline, const char *msg)
+{
+	switch (kline->hd.cmd) {
+		case KLN_CMD_WORK:
+			applog(READ_DEBUG,
+				"%s%i:%d %s work [%c] dev=%d workid=0x%02x ...",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->wt.dev), msg, kline->ws.cmd,
+				(int)(kline->wt.dev),
+				(int)(kline->wt.workid));
+			break;
+		case KLN_CMD_CONFIG:
+			applog(READ_DEBUG,
+				"%s%i:%d %s config [%c] dev=%d clock=%d"
+				" temptarget=%d tempcrit=%d fan=%d",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->cfg.dev), msg, kline->cfg.cmd,
+				(int)(kline->cfg.dev),
+				K_HASHCLOCK(kline->cfg.hashclock),
+				(int)(kline->cfg.temptarget),
+				(int)(kline->cfg.tempcritical),
+				(int)(kline->cfg.fantarget));
+			break;
+		case KLN_CMD_IDENT:
+		case KLN_CMD_STATUS:
+		case KLN_CMD_ABORT:
+			applog(READ_DEBUG,
+				"%s%i:%d %s cmd [%c]",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->hd.dev), msg, kline->hd.cmd);
+			break;
+		case KLN_CMD_ENABLE:
+			applog(READ_DEBUG,
+				"%s%i:%d %s enable [%c] enable=%c",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->hd.dev), msg, kline->hd.cmd,
+				(char)(kline->hd.buf[0]));
+			break;
+		case KLN_CMD_NONCE:
+		default:
+		{
+			char hexdata[REPLY_SIZE * 2];
+			bin2hex(hexdata, (unsigned char *)&(kline->hd.dev), REPLY_SIZE - 1);
+			applog(LOG_ERR,
+				"%s%i:%d %s [%c:%s] unknown/unexpected and ignored",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->hd.dev), msg, kline->hd.cmd,
+				hexdata);
+			break;
+		}
+	}
+}
+
+static bool SendCmd(struct cgpu_info *klncgpu, KLINE *kline, int datalen)
+{
+	struct klondike_info * const klninfo = klncgpu->device_data;
+	int err, amt, writ;
+
+	if (klninfo->usbinfo_nodev)
+		return false;
+
+	display_send_kline(klncgpu, kline, msg_send);
+	writ = KSENDHD(datalen);
+	err = usb_write(klncgpu, kline, writ, &amt);
+	if (err < 0 || amt != writ) {
+		applog(LOG_ERR, "%s%i:%d Cmd:%c Dev:%d, write failed (%d:%d:%d)",
+				klncgpu->drv->name, klncgpu->device_id,
+				(int)(kline->hd.dev),
+				kline->hd.cmd, (int)(kline->hd.dev),
+				writ, amt, err);
+		return false;
+	}
+
+	return true;
+}
+
+static KLIST *GetReply(struct cgpu_info *klncgpu, uint8_t cmd, uint8_t dev)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	KLIST *kitem;
+	int retries = CMD_REPLY_RETRIES;
+
+	while (retries-- > 0 && klncgpu->shutdown == false) {
+		cgsleep_ms(REPLY_WAIT_TIME);
+		cg_rlock(&klninfo->klist_lock);
+		kitem = klninfo->used;
+		while (kitem) {
+			if (kitem->kline.hd.cmd == cmd &&
+			    kitem->kline.hd.dev == dev &&
+			    kitem->ready == true && kitem->working == false) {
+				kitem->working = true;
+				cg_runlock(&klninfo->klist_lock);
+				return kitem;
+			}
+			kitem = kitem->next;
+		}
+		cg_runlock(&klninfo->klist_lock);
+	}
+	return NULL;
+}
+
+static KLIST *SendCmdGetReply(struct cgpu_info *klncgpu, KLINE *kline, int datalen)
+{
+	if (!SendCmd(klncgpu, kline, datalen))
+		return NULL;
+
+	return GetReply(klncgpu, kline->hd.cmd, kline->hd.dev);
+}
+
+static bool klondike_get_stats(struct cgpu_info *klncgpu)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	KLIST *kitem;
+	KLINE kline;
+	int slaves, dev;
+	uint8_t temp = 0xFF;
+
+	if (klninfo->usbinfo_nodev || klninfo->status == NULL)
+		return false;
+
+	applog(LOG_DEBUG, "%s%i: getting status",
+			klncgpu->drv->name, klncgpu->device_id);
+
+	rd_lock(&(klninfo->stat_lock));
+	slaves = klninfo->status[0].kline.ws.slavecount;
+	rd_unlock(&(klninfo->stat_lock));
+
+	// loop thru devices and get status for each
+	for (dev = 0; dev <= slaves; dev++) {
+		zero_kline(&kline);
+		kline.hd.cmd = KLN_CMD_STATUS;
+		kline.hd.dev = dev;
+		kitem = SendCmdGetReply(klncgpu, &kline, 0);
+		if (kitem != NULL) {
+			wr_lock(&(klninfo->stat_lock));
+			memcpy((void *)(&(klninfo->status[dev])),
+				(void *)kitem,
+				sizeof(klninfo->status[dev]));
+			wr_unlock(&(klninfo->stat_lock));
+			kitem = release_kitem(klncgpu, kitem);
+		} else {
+			applog(LOG_ERR, "%s%i:%d failed to update stats",
+					klncgpu->drv->name, klncgpu->device_id, dev);
+		}
+		if (klninfo->status[dev].kline.ws.temp < temp)
+			temp = klninfo->status[dev].kline.ws.temp;
+	}
+	klncgpu->temp = cvtKlnToC(temp);
+	return true;
+}
+
+// TODO: this only enables the master (no slaves)
+static bool kln_enable(struct cgpu_info *klncgpu)
+{
+	KLIST *kitem;
+	KLINE kline;
+	int tries = 2;
+	bool ok = false;
+
+	zero_kline(&kline);
+	kline.hd.cmd = KLN_CMD_ENABLE;
+	kline.hd.dev = 0;
+	kline.hd.buf[0] = KLN_CMD_ENABLE_ON;
+	
+	while (tries-- > 0) {
+		kitem = SendCmdGetReply(klncgpu, &kline, 1);
+		if (kitem) {
+			kitem = release_kitem(klncgpu, kitem);
+			ok = true;
+			break;
+		}
+		cgsleep_ms(50);
+	}
+
+	if (ok)
+		cgsleep_ms(50);
+
+	return ok;
+}
+
+static void kln_disable(struct cgpu_info *klncgpu, int dev, bool all)
+{
+	KLINE kline;
+	int i;
+
+	zero_kline(&kline);
+	kline.hd.cmd = KLN_CMD_ENABLE;
+	kline.hd.buf[0] = KLN_CMD_ENABLE_OFF;
+	for (i = (all ? 0 : dev); i <= dev; i++) {
+		kline.hd.dev = i;
+		SendCmd(klncgpu, &kline, KSENDHD(1));
+	}
+}
+
+static bool klondike_init(struct cgpu_info *klncgpu)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	KLIST *kitem;
+	KLINE kline;
+	int slaves, dev;
+
+	klninfo->initialised = false;
+
+	zero_kline(&kline);
+	kline.hd.cmd = KLN_CMD_STATUS;
+	kline.hd.dev = 0;
+	kitem = SendCmdGetReply(klncgpu, &kline, 0);
+	if (kitem == NULL)
+		return false;
+
+	slaves = kitem->kline.ws.slavecount;
+	if (klninfo->status == NULL) {
+		applog(LOG_DEBUG, "%s%i: initializing data",
+				klncgpu->drv->name, klncgpu->device_id);
+
+		// alloc space for status, devinfo, cfg and jobque for master and slaves
+		klninfo->status = calloc(slaves+1, sizeof(*(klninfo->status)));
+		if (unlikely(!klninfo->status))
+			quit(1, "Failed to calloc status array in klondke_get_stats");
+		klninfo->devinfo = calloc(slaves+1, sizeof(*(klninfo->devinfo)));
+		if (unlikely(!klninfo->devinfo))
+			quit(1, "Failed to calloc devinfo array in klondke_get_stats");
+		klninfo->cfg = calloc(slaves+1, sizeof(*(klninfo->cfg)));
+		if (unlikely(!klninfo->cfg))
+			quit(1, "Failed to calloc cfg array in klondke_get_stats");
+		klninfo->jobque = calloc(slaves+1, sizeof(*(klninfo->jobque)));
+		if (unlikely(!klninfo->jobque))
+			quit(1, "Failed to calloc jobque array in klondke_get_stats");
+	}
+
+	memcpy((void *)(&(klninfo->status[0])), (void *)kitem, sizeof(klninfo->status[0]));
+	kitem = release_kitem(klncgpu, kitem);
+
+	// zero init triggers read back only
+	zero_kline(&kline);
+	kline.cfg.cmd = KLN_CMD_CONFIG;
+
+	int size = 2;
+
+	// boundaries are checked by device, with valid values returned
+	if (opt_klondike_options != NULL) {
+		int hashclock;
+		double temptarget;
+
+		sscanf(opt_klondike_options, "%d:%lf", &hashclock, &temptarget);
+		SET_HASHCLOCK(kline.cfg.hashclock, hashclock);
+		kline.cfg.temptarget = cvtCToKln(temptarget);
+		kline.cfg.tempcritical = 0; // hard code for old firmware
+		kline.cfg.fantarget = 0xff; // hard code for old firmware
+		size = sizeof(kline.cfg) - 2;
+	}
+
+	for (dev = 0; dev <= slaves; dev++) {
+		kline.cfg.dev = dev;
+		kitem = SendCmdGetReply(klncgpu, &kline, size);
+		if (kitem != NULL) {
+			memcpy((void *)&(klninfo->cfg[dev]), kitem, sizeof(klninfo->cfg[dev]));
+			applog(LOG_WARNING, "%s%i:%d config (%d: Clk: %d, T:%.0lf, C:%.0lf, F:%d)",
+				klncgpu->drv->name, klncgpu->device_id, dev,
+				dev, K_HASHCLOCK(klninfo->cfg[dev].kline.cfg.hashclock),
+				cvtKlnToC(klninfo->cfg[dev].kline.cfg.temptarget),
+				cvtKlnToC(klninfo->cfg[dev].kline.cfg.tempcritical),
+				(int)100*klninfo->cfg[dev].kline.cfg.fantarget/256);
+			kitem = release_kitem(klncgpu, kitem);
+		}
+	}
+	klondike_get_stats(klncgpu);
+	klninfo->initialised = true;
+	for (dev = 0; dev <= slaves; dev++) {
+		klninfo->devinfo[dev].rangesize = ((uint64_t)1<<32) / klninfo->status[dev].kline.ws.chipcount;
+		klninfo->devinfo[dev].chipstats = calloc(klninfo->status[dev].kline.ws.chipcount*2 , sizeof(uint32_t));
+	}
+
+	bool ok = kln_enable(klncgpu);
+
+	if (!ok)
+		applog(LOG_ERR, "%s%i: failed to enable", klncgpu->drv->name, klncgpu->device_id);
+
+	return ok;
+}
+
+static void control_init(struct cgpu_info *klncgpu)
+{
+	struct klondike_info * const klninfo = klncgpu->device_data;
+	int err, interface;
+
+	if (klninfo->usbinfo_nodev)
+		return;
+
+	interface = 0;
+
+	err = libusb_control_transfer(klninfo->usbdev_handle, 0, 9, 1, interface, NULL, 0, 999);
+
+	applog(LOG_DEBUG, "%s%i: reset got err %d",
+			  klncgpu->drv->name, klncgpu->device_id, err);
+}
+
+static
+bool klondike_foundlowl(struct lowlevel_device_info * const info, __maybe_unused void * const userp)
+{
+	if (unlikely(info->lowl != &lowl_usb))
+	{
+		applog(LOG_WARNING, "%s: Matched \"%s\" serial \"%s\", but lowlevel driver is not usb!",
+		       __func__, info->product, info->serial);
+		return false;
+	}
+	struct libusb_device * const dev = info->lowl_data;
+	
+// static bool klondike_detect_one(struct libusb_device *dev, struct usb_find_devices *found)
+	struct cgpu_info * const klncgpu = malloc(sizeof(*klncgpu));
+	struct klondike_info *klninfo = NULL;
+	KLINE kline;
+
+	if (unlikely(!klncgpu))
+		quit(1, "Failed to calloc klncgpu in klondike_detect_one");
+	
+	*klncgpu = (struct cgpu_info){
+		.drv = &klondike_drv,
+		.deven = DEV_ENABLED,
+		.threads = 1,
+		.cutofftemp = (int)KLN_KILLWORK_TEMP,
+	};
+
+	klninfo = calloc(1, sizeof(*klninfo));
+	if (unlikely(!klninfo))
+		quit(1, "Failed to calloc klninfo in klondke_detect_one");
+	klncgpu->device_data = (void *)klninfo;
+
+	klninfo->free = new_klist_set(klncgpu);
+
+	if (usb_init(klncgpu, dev)) {
+		int sent, recd, err;
+		KLIST kitem;
+		int attempts = 0;
+		
+		klncgpu->device_path = strdup(info->devid);
+
+		control_init(klncgpu);
+
+		while (attempts++ < 3) {
+			kline.hd.cmd = KLN_CMD_IDENT;
+			kline.hd.dev = 0;
+			display_send_kline(klncgpu, &kline, msg_detect_send);
+			err = usb_write(klncgpu, (char *)&(kline.hd), 2, &sent);
+			if (err < 0 || sent != 2) {
+				applog(LOG_ERR, "%s (%s) detect write failed (%d:%d)",
+						klncgpu->drv->dname,
+						klncgpu->device_path,
+						sent, err);
+			}
+			cgsleep_ms(REPLY_WAIT_TIME*10);
+			err = usb_read(klncgpu, &kitem.kline, REPLY_SIZE, &recd);
+			if (err < 0) {
+				applog(LOG_ERR, "%s (%s) detect read failed (%d:%d)",
+						klncgpu->drv->dname,
+						klncgpu->device_path,
+						recd, err);
+			} else if (recd < 1) {
+				applog(LOG_ERR, "%s (%s) detect empty reply (%d)",
+						klncgpu->drv->dname,
+						klncgpu->device_path,
+						recd);
+			} else if (kitem.kline.hd.cmd == KLN_CMD_IDENT && kitem.kline.hd.dev == 0) {
+				display_kline(klncgpu, &kitem.kline, msg_detect_reply);
+				applog(LOG_DEBUG, "%s (%s) detect successful (%d attempt%s)",
+						  klncgpu->drv->dname,
+						  klncgpu->device_path,
+						  attempts, attempts == 1 ? "" : "s");
+				if (!add_cgpu(klncgpu))
+					break;
+				applog(LOG_DEBUG, "Klondike cgpu added");
+				rwlock_init(&klninfo->stat_lock);
+				cglock_init(&klninfo->klist_lock);
+				return true;
+			}
+		}
+		usb_uninit(klncgpu);
+	}
+	free(klninfo->free);
+	free(klninfo);
+	free(klncgpu);
+	return false;
+}
+
+static
+bool klondike_detect_one(const char *serial)
+{
+	return lowlevel_detect_serial(klondike_foundlowl, serial);
+}
+
+static
+bool klondike_foundlowl_checkmanuf(struct lowlevel_device_info * const info, void * const userp)
+{
+	if (info->manufacturer && strstr(info->manufacturer, "Klondike"))
+		return klondike_foundlowl(info, userp);
+	return false;
+}
+
+static
+int klondike_autodetect()
+{
+	return lowlevel_detect_id(klondike_foundlowl_checkmanuf, NULL, &lowl_usb, 0x04d8, 0xf60a);
+}
+
+static
+void klondike_detect()
+{
+	generic_detect(&klondike_drv, klondike_detect_one, klondike_autodetect, 0);
+}
+
+static void klondike_check_nonce(struct cgpu_info *klncgpu, KLIST *kitem)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	struct work *work, *look, *tmp;
+	KLINE *kline = &(kitem->kline);
+	struct timeval tv_now;
+	double us_diff;
+	uint32_t nonce = K_NONCE(kline->wr.nonce) - 0xC0;
+
+	applog(LOG_DEBUG, "%s%i:%d FOUND NONCE (%02x:%08x)",
+			  klncgpu->drv->name, klncgpu->device_id, (int)(kline->wr.dev),
+			  kline->wr.workid, (unsigned int)nonce);
+
+	work = NULL;
+	cgtime(&tv_now);
+	rd_lock(&(klncgpu->qlock));
+	HASH_ITER(hh, klncgpu->queued_work, look, tmp) {
+		if (ms_tdiff(&tv_now, &(look->tv_stamp)) < OLD_WORK_MS &&
+		    (look->subid == (kline->wr.dev*256 + kline->wr.workid))) {
+			work = look;
+			break;
+		}
+	}
+	rd_unlock(&(klncgpu->qlock));
+
+	if (work) {
+		wr_lock(&(klninfo->stat_lock));
+		klninfo->devinfo[kline->wr.dev].noncecount++;
+		klninfo->noncecount++;
+		wr_unlock(&(klninfo->stat_lock));
+
+		applog(LOG_DEBUG, "%s%i:%d SUBMIT NONCE (%02x:%08x)",
+				  klncgpu->drv->name, klncgpu->device_id, (int)(kline->wr.dev),
+				  kline->wr.workid, (unsigned int)nonce);
+
+		cgtime(&tv_now);
+		bool ok = submit_nonce(klncgpu->thr[0], work, nonce);
+
+		applog(LOG_DEBUG, "%s%i:%d chip stats %d, %08x, %d, %d",
+				  klncgpu->drv->name, klncgpu->device_id, (int)(kline->wr.dev),
+				  kline->wr.dev, (unsigned int)nonce,
+				  klninfo->devinfo[kline->wr.dev].rangesize,
+				  klninfo->status[kline->wr.dev].kline.ws.chipcount);
+
+		klninfo->devinfo[kline->wr.dev].chipstats[(nonce / klninfo->devinfo[kline->wr.dev].rangesize) + (ok ? 0 : klninfo->status[kline->wr.dev].kline.ws.chipcount)]++;
+
+		us_diff = us_tdiff(&tv_now, &(kitem->tv_when));
+		if (klninfo->delay_count == 0) {
+			klninfo->delay_min = us_diff;
+			klninfo->delay_max = us_diff;
+		} else {
+			if (klninfo->delay_min > us_diff)
+				klninfo->delay_min = us_diff;
+			if (klninfo->delay_max < us_diff)
+				klninfo->delay_max = us_diff;
+		}
+		klninfo->delay_count++;
+		klninfo->delay_total += us_diff;
+
+		if (klninfo->nonce_count > 0) {
+			us_diff = us_tdiff(&(kitem->tv_when), &(klninfo->tv_last_nonce_received));
+			if (klninfo->nonce_count == 1) {
+				klninfo->nonce_min = us_diff;
+				klninfo->nonce_max = us_diff;
+			} else {
+				if (klninfo->nonce_min > us_diff)
+					klninfo->nonce_min = us_diff;
+				if (klninfo->nonce_max < us_diff)
+					klninfo->nonce_max = us_diff;
+			}
+			klninfo->nonce_total += us_diff;
+		}
+		klninfo->nonce_count++;
+
+		memcpy(&(klninfo->tv_last_nonce_received), &(kitem->tv_when),
+			sizeof(klninfo->tv_last_nonce_received));
+
+		return;
+	}
+
+	applog(LOG_ERR, "%s%i:%d unknown work (%02x:%08x) - ignored",
+			klncgpu->drv->name, klncgpu->device_id, (int)(kline->wr.dev),
+			kline->wr.workid, (unsigned int)nonce);
+
+	//inc_hw_errors(klncgpu->thr[0]);
+}
+
+// thread to keep looking for replies
+static void *klondike_get_replies(void *userdata)
+{
+	struct cgpu_info *klncgpu = (struct cgpu_info *)userdata;
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	KLIST *kitem = NULL;
+	int err, recd, slaves, dev, isc;
+	bool overheat, sent;
+
+	applog(LOG_DEBUG, "%s%i: listening for replies",
+			  klncgpu->drv->name, klncgpu->device_id);
+
+	while (klncgpu->shutdown == false) {
+		if (klninfo->usbinfo_nodev)
+			return NULL;
+
+		if (kitem == NULL)
+			kitem = allocate_kitem(klncgpu);
+		else
+			memset((void *)&(kitem->kline), 0, sizeof(kitem->kline));
+
+		err = usb_read(klncgpu, &kitem->kline, REPLY_SIZE, &recd);
+		if (err || recd != REPLY_SIZE) {
+			if (err != -7)
+				applog(LOG_ERR, "%s%i: reply err=%d amt=%d",
+						klncgpu->drv->name, klncgpu->device_id,
+						err, recd);
+		}
+		if (!err && recd == REPLY_SIZE) {
+			cgtime(&(kitem->tv_when));
+			rd_lock(&(klninfo->stat_lock));
+			kitem->block_seq = klninfo->block_seq;
+			rd_unlock(&(klninfo->stat_lock));
+			if (opt_log_level <= READ_DEBUG) {
+				char hexdata[recd * 2];
+				bin2hex(hexdata, &kitem->kline.hd.dev, recd-1);
+				applog(READ_DEBUG, "%s%i:%d reply [%c:%s]",
+						klncgpu->drv->name, klncgpu->device_id,
+						(int)(kitem->kline.hd.dev),
+						kitem->kline.hd.cmd, hexdata);
+			}
+
+			// We can't check this until it's initialised
+			if (klninfo->initialised) {
+				rd_lock(&(klninfo->stat_lock));
+				slaves = klninfo->status[0].kline.ws.slavecount;
+				rd_unlock(&(klninfo->stat_lock));
+
+				if (kitem->kline.hd.dev > slaves) {
+					applog(LOG_ERR, "%s%i: reply [%c] has invalid dev=%d (max=%d) using 0",
+							klncgpu->drv->name, klncgpu->device_id,
+							(char)(kitem->kline.hd.cmd),
+							(int)(kitem->kline.hd.dev),
+							slaves);
+					/* TODO: this is rather problematic if there are slaves
+					 * however without slaves - it should always be zero */
+					kitem->kline.hd.dev = 0;
+				} else {
+					wr_lock(&(klninfo->stat_lock));
+					klninfo->jobque[kitem->kline.hd.dev].late_update_sequential = 0;
+					wr_unlock(&(klninfo->stat_lock));
+				}
+			}
+
+			switch (kitem->kline.hd.cmd) {
+				case KLN_CMD_NONCE:
+					klondike_check_nonce(klncgpu, kitem);
+					display_kline(klncgpu, &kitem->kline, msg_reply);
+					break;
+				case KLN_CMD_WORK:
+					// We can't do/check this until it's initialised
+					if (klninfo->initialised) {
+						dev = kitem->kline.ws.dev;
+						if (kitem->kline.ws.workqc == 0) {
+							bool idle = false;
+							rd_lock(&(klninfo->stat_lock));
+							if (klninfo->jobque[dev].flushed == false)
+								idle = true;
+							slaves = klninfo->status[0].kline.ws.slavecount;
+							rd_unlock(&(klninfo->stat_lock));
+							if (idle)
+								applog(LOG_WARNING, "%s%i:%d went idle before work was sent",
+										    klncgpu->drv->name,
+										    klncgpu->device_id,
+										    dev);
+						}
+						wr_lock(&(klninfo->stat_lock));
+						klninfo->jobque[dev].flushed = false;
+						wr_unlock(&(klninfo->stat_lock));
+					}
+				case KLN_CMD_STATUS:
+				case KLN_CMD_ABORT:
+					// We can't do/check this until it's initialised
+					if (klninfo->initialised) {
+						isc = 0;
+						dev = kitem->kline.ws.dev;
+						wr_lock(&(klninfo->stat_lock));
+						klninfo->jobque[dev].workqc = (int)(kitem->kline.ws.workqc);
+						cgtime(&(klninfo->jobque[dev].last_update));
+						slaves = klninfo->status[0].kline.ws.slavecount;
+						overheat = klninfo->jobque[dev].overheat;
+						if (dev == 0) {
+							if (kitem->kline.ws.slavecount != slaves)
+								isc = ++klninfo->incorrect_slave_sequential;
+							else
+								isc = klninfo->incorrect_slave_sequential = 0;
+						}
+						wr_unlock(&(klninfo->stat_lock));
+
+						if (isc) {
+							applog(LOG_ERR, "%s%i:%d reply [%c] has a diff"
+									" # of slaves=%d (curr=%d)%s",
+									klncgpu->drv->name,
+									klncgpu->device_id,
+									dev,
+									(char)(kitem->kline.ws.cmd),
+									(int)(kitem->kline.ws.slavecount),
+									slaves,
+									isc <= KLN_ISS_IGNORE ? "" :
+									 " disabling device");
+							if (isc > KLN_ISS_IGNORE)
+								usb_nodev(klncgpu);
+							break;
+						}
+
+						if (!overheat) {
+							double temp = cvtKlnToC(kitem->kline.ws.temp);
+							if (temp >= KLN_KILLWORK_TEMP) {
+								KLINE kline;
+
+								wr_lock(&(klninfo->stat_lock));
+								klninfo->jobque[dev].overheat = true;
+								wr_unlock(&(klninfo->stat_lock));
+
+								applog(LOG_WARNING, "%s%i:%d Critical overheat (%.0fC)",
+										    klncgpu->drv->name,
+										    klncgpu->device_id,
+										    dev, temp);
+
+								zero_kline(&kline);
+								kline.hd.cmd = KLN_CMD_ABORT;
+								kline.hd.dev = dev;
+								sent = SendCmd(klncgpu, &kline, KSENDHD(0));
+								kln_disable(klncgpu, dev, false);
+								if (!sent) {
+									applog(LOG_ERR, "%s%i:%d overheat failed to"
+											" abort work - disabling device",
+											klncgpu->drv->name,
+											klncgpu->device_id,
+											dev);
+									usb_nodev(klncgpu);
+								}
+							}
+						}
+					}
+				case KLN_CMD_ENABLE:
+					wr_lock(&(klninfo->stat_lock));
+					klninfo->errorcount += kitem->kline.ws.errorcount;
+					klninfo->noisecount += kitem->kline.ws.noise;
+					wr_unlock(&(klninfo->stat_lock));
+					display_kline(klncgpu, &kitem->kline, msg_reply);
+					kitem->ready = true;
+					kitem = NULL;
+					break;
+				case KLN_CMD_CONFIG:
+					display_kline(klncgpu, &kitem->kline, msg_reply);
+					kitem->ready = true;
+					kitem = NULL;
+					break;
+				case KLN_CMD_IDENT:
+					display_kline(klncgpu, &kitem->kline, msg_reply);
+					kitem->ready = true;
+					kitem = NULL;
+					break;
+				default:
+					display_kline(klncgpu, &kitem->kline, msg_reply);
+					break;
+			}
+		}
+	}
+	return NULL;
+}
+
+static void klondike_flush_work(struct cgpu_info *klncgpu)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	KLIST *kitem;
+	KLINE kline;
+	int slaves, dev;
+
+	wr_lock(&(klninfo->stat_lock));
+	klninfo->block_seq++;
+	slaves = klninfo->status[0].kline.ws.slavecount;
+	wr_unlock(&(klninfo->stat_lock));
+
+	applog(LOG_DEBUG, "%s%i: flushing work",
+			  klncgpu->drv->name, klncgpu->device_id);
+	zero_kline(&kline);
+	kline.hd.cmd = KLN_CMD_ABORT;
+	for (dev = 0; dev <= slaves; dev++) {
+		kline.hd.dev = dev;
+		kitem = SendCmdGetReply(klncgpu, &kline, KSENDHD(0));
+		if (kitem != NULL) {
+			wr_lock(&(klninfo->stat_lock));
+			memcpy((void *)&(klninfo->status[dev]),
+				kitem,
+				sizeof(klninfo->status[dev]));
+			klninfo->jobque[dev].flushed = true;
+			wr_unlock(&(klninfo->stat_lock));
+			kitem = release_kitem(klncgpu, kitem);
+		}
+	}
+}
+
+static bool klondike_thread_prepare(struct thr_info *thr)
+{
+	struct cgpu_info *klncgpu = thr->cgpu;
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+
+	if (thr_info_create(&(klninfo->replies_thr), NULL, klondike_get_replies, (void *)klncgpu)) {
+		applog(LOG_ERR, "%s%i: thread create failed", klncgpu->drv->name, klncgpu->device_id);
+		return false;
+	}
+	pthread_detach(klninfo->replies_thr.pth);
+
+	// let the listening get started
+	cgsleep_ms(100);
+
+	return klondike_init(klncgpu);
+}
+
+static bool klondike_thread_init(struct thr_info *thr)
+{
+	struct cgpu_info *klncgpu = thr->cgpu;
+	struct klondike_info * const klninfo = klncgpu->device_data;
+	
+	notifier_init(thr->work_restart_notifier);
+
+	if (klninfo->usbinfo_nodev)
+		return false;
+
+	klondike_flush_work(klncgpu);
+
+	return true;
+}
+
+static void klondike_shutdown(struct thr_info *thr)
+{
+	struct cgpu_info *klncgpu = thr->cgpu;
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+
+	applog(LOG_DEBUG, "%s%i: shutting down work",
+			  klncgpu->drv->name, klncgpu->device_id);
+
+	kln_disable(klncgpu, klninfo->status[0].kline.ws.slavecount, true);
+
+	klncgpu->shutdown = true;
+}
+
+static void klondike_thread_enable(struct thr_info *thr)
+{
+	struct cgpu_info *klncgpu = thr->cgpu;
+	struct klondike_info * const klninfo = klncgpu->device_data;
+
+	if (klninfo->usbinfo_nodev)
+		return;
+
+/*
+	KLINE kline;
+
+	zero_kline(&kline);
+	kline.hd.cmd = KLN_CMD_ENABLE;
+	kline.hd.dev = dev;
+	kline.hd.buf[0] = KLN_CMD_ENABLE_OFF;
+	kitem = SendCmdGetReply(klncgpu, &kline, KSENDHD(1));
+*/
+
+}
+
+static bool klondike_send_work(struct cgpu_info *klncgpu, int dev, struct work *work)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	struct work *look, *tmp;
+	KLINE kline;
+	struct timeval tv_old;
+	int wque_size, wque_cleared;
+
+	if (klninfo->usbinfo_nodev)
+		return false;
+
+	zero_kline(&kline);
+	kline.wt.cmd = KLN_CMD_WORK;
+	kline.wt.dev = dev;
+	memcpy(kline.wt.midstate, work->midstate, MIDSTATE_BYTES);
+	memcpy(kline.wt.merkle, work->data + MERKLE_OFFSET, MERKLE_BYTES);
+	kline.wt.workid = (uint8_t)(klninfo->devinfo[dev].nextworkid++ & 0xFF);
+	work->subid = dev*256 + kline.wt.workid;
+	cgtime(&work->tv_stamp);
+
+	if (opt_log_level <= LOG_DEBUG) {
+		char hexdata[(sizeof(kline.wt) * 2) + 1];
+		bin2hex(hexdata, &kline.wt, sizeof(kline.wt));
+		applog(LOG_DEBUG, "WORKDATA: %s", hexdata);
+	}
+
+	applog(LOG_DEBUG, "%s%i:%d sending work (%d:%02x)",
+			  klncgpu->drv->name, klncgpu->device_id, dev,
+			  dev, kline.wt.workid);
+	KLIST *kitem = SendCmdGetReply(klncgpu, &kline, sizeof(kline.wt));
+	if (kitem != NULL) {
+		wr_lock(&(klninfo->stat_lock));
+		memcpy((void *)&(klninfo->status[dev]), kitem, sizeof(klninfo->status[dev]));
+		wr_unlock(&(klninfo->stat_lock));
+		kitem = release_kitem(klncgpu, kitem);
+
+		// remove old work
+		wque_size = 0;
+		wque_cleared = 0;
+		cgtime(&tv_old);
+		wr_lock(&klncgpu->qlock);
+		HASH_ITER(hh, klncgpu->queued_work, look, tmp) {
+			if (ms_tdiff(&tv_old, &(look->tv_stamp)) > OLD_WORK_MS) {
+				__work_completed(klncgpu, look);
+				free_work(look);
+				wque_cleared++;
+			} else
+				wque_size++;
+		}
+		wr_unlock(&klncgpu->qlock);
+
+		wr_lock(&(klninfo->stat_lock));
+		klninfo->wque_size = wque_size;
+		klninfo->wque_cleared = wque_cleared;
+		wr_unlock(&(klninfo->stat_lock));
+		return true;
+	}
+	return false;
+}
+
+static bool klondike_queue_full(struct cgpu_info *klncgpu)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	struct work *work = NULL;
+	int dev, queued, slaves, seq, howlong;
+	struct timeval now;
+	bool nowork;
+
+	if (klncgpu->shutdown == true)
+		return true;
+
+	cgtime(&now);
+	rd_lock(&(klninfo->stat_lock));
+	slaves = klninfo->status[0].kline.ws.slavecount;
+	for (dev = 0; dev <= slaves; dev++)
+		if (ms_tdiff(&now, &(klninfo->jobque[dev].last_update)) > LATE_UPDATE_MS) {
+			klninfo->jobque[dev].late_update_count++;
+			seq = ++klninfo->jobque[dev].late_update_sequential;
+			rd_unlock(&(klninfo->stat_lock));
+			if (seq < LATE_UPDATE_LIMIT) {
+				applog(LOG_DEBUG, "%s%i:%d late update",
+						klncgpu->drv->name, klncgpu->device_id, dev);
+				klondike_get_stats(klncgpu);
+				goto que;
+			} else {
+				applog(LOG_WARNING, "%s%i:%d late update (%d) reached - attempting reset",
+						    klncgpu->drv->name, klncgpu->device_id,
+						    dev, LATE_UPDATE_LIMIT);
+				control_init(klncgpu);
+				kln_enable(klncgpu);
+				klondike_get_stats(klncgpu);
+				rd_lock(&(klninfo->stat_lock));
+				howlong = ms_tdiff(&now, &(klninfo->jobque[dev].last_update));
+				if (howlong > LATE_UPDATE_MS) {
+					rd_unlock(&(klninfo->stat_lock));
+					if (howlong > LATE_UPDATE_NODEV_MS) {
+						applog(LOG_ERR, "%s%i:%d reset failed - dropping device",
+								klncgpu->drv->name, klncgpu->device_id, dev);
+						usb_nodev(klncgpu);
+					} else
+						cgsleep_ms(LATE_UPDATE_SLEEP_MS);
+
+					return true;
+				}
+				break;
+			}
+		}
+	rd_unlock(&(klninfo->stat_lock));
+
+que:
+
+	nowork = true;
+	for (queued = 0; queued < MAX_WORK_COUNT-1; queued++)
+		for (dev = 0; dev <= slaves; dev++) {
+tryagain:
+			rd_lock(&(klninfo->stat_lock));
+			if (klninfo->jobque[dev].overheat) {
+				double temp = cvtKlnToC(klninfo->status[0].kline.ws.temp);
+				if ((queued == MAX_WORK_COUNT-2) &&
+				    ms_tdiff(&now, &(klninfo->jobque[dev].last_update)) > (LATE_UPDATE_MS/2)) {
+					rd_unlock(&(klninfo->stat_lock));
+					klondike_get_stats(klncgpu);
+					goto tryagain;
+				}
+				if (temp <= KLN_COOLED_DOWN) {
+					klninfo->jobque[dev].overheat = false;
+					rd_unlock(&(klninfo->stat_lock));
+					applog(LOG_WARNING, "%s%i:%d Overheat recovered (%.0fC)",
+							    klncgpu->drv->name, klncgpu->device_id,
+							    dev, temp);
+					kln_enable(klncgpu);
+					goto tryagain;
+				} else {
+					rd_unlock(&(klninfo->stat_lock));
+					continue;
+				}
+			}
+
+			if (klninfo->jobque[dev].workqc <= queued) {
+				rd_unlock(&(klninfo->stat_lock));
+				if (!work)
+					work = get_queued(klncgpu);
+				if (unlikely(!work))
+					return false;
+				nowork = false;
+				if (klondike_send_work(klncgpu, dev, work))
+					return false;
+			} else
+				rd_unlock(&(klninfo->stat_lock));
+		}
+
+	if (nowork)
+		cgsleep_ms(10); // avoid a hard loop in case we have nothing to do
+
+	return true;
+}
+
+static int64_t klondike_scanwork(struct thr_info *thr)
+{
+	struct cgpu_info *klncgpu = thr->cgpu;
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	int64_t newhashcount = 0;
+	int dev, slaves;
+
+	if (klninfo->usbinfo_nodev)
+		return -1;
+
+	restart_wait(thr, 200);
+	if (klninfo->status != NULL) {
+		rd_lock(&(klninfo->stat_lock));
+		slaves = klninfo->status[0].kline.ws.slavecount;
+		for (dev = 0; dev <= slaves; dev++) {
+			uint64_t newhashdev = 0, hashcount;
+			int maxcount;
+
+			hashcount = K_HASHCOUNT(klninfo->status[dev].kline.ws.hashcount);
+			maxcount = K_MAXCOUNT(klninfo->status[dev].kline.ws.maxcount);
+			// todo: chg this to check workid for wrapped instead
+			if (klninfo->devinfo[dev].lasthashcount > hashcount)
+				newhashdev += maxcount; // hash counter wrapped
+			newhashdev += hashcount - klninfo->devinfo[dev].lasthashcount;
+			klninfo->devinfo[dev].lasthashcount = hashcount;
+			if (maxcount != 0)
+				klninfo->hashcount += (newhashdev << 32) / maxcount;
+		}
+		newhashcount += 0xffffffffull * (uint64_t)klninfo->noncecount;
+		klninfo->noncecount = 0;
+		rd_unlock(&(klninfo->stat_lock));
+	}
+
+	return newhashcount;
+}
+
+
+#ifdef HAVE_CURSES
+static
+void klondike_wlogprint_status(struct cgpu_info *klncgpu)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	uint16_t fan = 0;
+	uint16_t clock = 0;
+	int dev, slaves;
+
+	if (klninfo->status == NULL) {
+		return;
+	}
+
+	rd_lock(&(klninfo->stat_lock));
+	slaves = klninfo->status[0].kline.ws.slavecount;
+	for (dev = 0; dev <= slaves; dev++) {
+		fan += klninfo->cfg[dev].kline.cfg.fantarget;
+		clock += (uint16_t)K_HASHCLOCK(klninfo->cfg[dev].kline.cfg.hashclock);
+	}
+	rd_unlock(&(klninfo->stat_lock));
+	fan /= slaves + 1;
+        fan = 100 * fan / 255;
+	clock /= slaves + 1;
+	if (clock && clock <= 999)
+		wlogprint("Frequency: %d MHz\n", (int)clock);
+	if (fan && fan <= 100)
+		wlogprint("Fan speed: %d%%\n", fan);
+}
+#endif
+
+static struct api_data *klondike_api_stats(struct cgpu_info *klncgpu)
+{
+	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
+	struct api_data *root = NULL;
+	char buf[32];
+	int dev, slaves;
+
+	if (klninfo->status == NULL)
+		return NULL;
+
+	rd_lock(&(klninfo->stat_lock));
+	slaves = klninfo->status[0].kline.ws.slavecount;
+	for (dev = 0; dev <= slaves; dev++) {
+
+		float fTemp = cvtKlnToC(klninfo->status[dev].kline.ws.temp);
+		sprintf(buf, "Temp %d", dev);
+		root = api_add_temp(root, buf, &fTemp, true);
+
+		double dClk = (double)K_HASHCLOCK(klninfo->cfg[dev].kline.cfg.hashclock);
+		sprintf(buf, "Clock %d", dev);
+		root = api_add_freq(root, buf, &dClk, true);
+
+		unsigned int iFan = (unsigned int)100 * klninfo->cfg[dev].kline.cfg.fantarget / 255;
+		sprintf(buf, "Fan Percent %d", dev);
+		root = api_add_int(root, buf, (int *)(&iFan), true);
+
+		iFan = 0;
+		if (klninfo->status[dev].kline.ws.fanspeed > 0)
+			iFan = (unsigned int)TACH_FACTOR / klninfo->status[dev].kline.ws.fanspeed;
+		sprintf(buf, "Fan RPM %d", dev);
+		root = api_add_int(root, buf, (int *)(&iFan), true);
+
+		if (klninfo->devinfo[dev].chipstats != NULL) {
+			char data[2048];
+			char one[32];
+			int n;
+
+			sprintf(buf, "Nonces / Chip %d", dev);
+			data[0] = '\0';
+			for (n = 0; n < klninfo->status[dev].kline.ws.chipcount; n++) {
+				snprintf(one, sizeof(one), "%07d ", klninfo->devinfo[dev].chipstats[n]);
+				strcat(data, one);
+			}
+			root = api_add_string(root, buf, data, true);
+
+			sprintf(buf, "Errors / Chip %d", dev);
+			data[0] = '\0';
+			for (n = 0; n < klninfo->status[dev].kline.ws.chipcount; n++) {
+				snprintf(one, sizeof(one), "%07d ", klninfo->devinfo[dev].chipstats[n + klninfo->status[dev].kline.ws.chipcount]);
+				strcat(data, one);
+			}
+			root = api_add_string(root, buf, data, true);
+		}
+	}
+
+	root = api_add_uint64(root, "Hash Count", &(klninfo->hashcount), true);
+	root = api_add_uint64(root, "Error Count", &(klninfo->errorcount), true);
+	root = api_add_uint64(root, "Noise Count", &(klninfo->noisecount), true);
+
+	root = api_add_int(root, "KLine Limit", &(klninfo->kline_count), true);
+	root = api_add_int(root, "KLine Used", &(klninfo->used_count), true);
+
+	root = api_add_elapsed(root, "KQue Delay Count", &(klninfo->delay_count), true);
+	root = api_add_elapsed(root, "KQue Delay Total", &(klninfo->delay_total), true);
+	root = api_add_elapsed(root, "KQue Delay Min", &(klninfo->delay_min), true);
+	root = api_add_elapsed(root, "KQue Delay Max", &(klninfo->delay_max), true);
+	double avg;
+	if (klninfo->delay_count == 0)
+		avg = 0;
+	else
+		avg = klninfo->delay_total / klninfo->delay_count;
+	root = api_add_diff(root, "KQue Delay Avg", &avg, true);
+
+	root = api_add_elapsed(root, "KQue Nonce Count", &(klninfo->nonce_count), true);
+	root = api_add_elapsed(root, "KQue Nonce Total", &(klninfo->nonce_total), true);
+	root = api_add_elapsed(root, "KQue Nonce Min", &(klninfo->nonce_min), true);
+	root = api_add_elapsed(root, "KQue Nonce Max", &(klninfo->nonce_max), true);
+	if (klninfo->nonce_count == 0)
+		avg = 0;
+	else
+		avg = klninfo->nonce_total / klninfo->nonce_count;
+	root = api_add_diff(root, "KQue Nonce Avg", &avg, true);
+
+	root = api_add_int(root, "WQue Size", &(klninfo->wque_size), true);
+	root = api_add_int(root, "WQue Cleared", &(klninfo->wque_cleared), true);
+
+	rd_unlock(&(klninfo->stat_lock));
+
+	return root;
+}
+
+struct device_drv klondike_drv = {
+	.dname = "Klondike",
+	.name = "KLN",
+	.drv_detect = klondike_detect,
+	.get_api_stats = klondike_api_stats,
+	.get_stats = klondike_get_stats,
+	.thread_prepare = klondike_thread_prepare,
+	.thread_init = klondike_thread_init,
+	.minerloop = hash_queued_work,
+	.scanwork = klondike_scanwork,
+	.queue_full = klondike_queue_full,
+	.flush_work = klondike_flush_work,
+	.thread_shutdown = klondike_shutdown,
+	.thread_enable = klondike_thread_enable,
+	
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = klondike_wlogprint_status,
+#endif
+};

+ 2 - 2
logging.h

@@ -103,10 +103,10 @@ extern void _applog(int prio, const char *str);
 	return rv;  \
 	return rv;  \
 } while (0)
 } while (0)
 
 
-extern void _bfg_clean_up(void);
+extern void _bfg_clean_up(bool);
 
 
 #define quit(status, fmt, ...) do { \
 #define quit(status, fmt, ...) do { \
-	_bfg_clean_up();  \
+	_bfg_clean_up(false);  \
 	if (fmt) { \
 	if (fmt) { \
 		fprintf(stderr, fmt, ##__VA_ARGS__);  \
 		fprintf(stderr, fmt, ##__VA_ARGS__);  \
 	} \
 	} \

+ 119 - 120
miner.c

@@ -222,6 +222,9 @@ bool opt_worktime;
 #ifdef USE_AVALON
 #ifdef USE_AVALON
 char *opt_avalon_options = NULL;
 char *opt_avalon_options = NULL;
 #endif
 #endif
+#ifdef USE_KLONDIKE
+char *opt_klondike_options = NULL;
+#endif
 
 
 char *opt_kernel_path;
 char *opt_kernel_path;
 char *cgminer_path;
 char *cgminer_path;
@@ -234,7 +237,6 @@ bool opt_bfl_noncerange;
 struct thr_info *control_thr;
 struct thr_info *control_thr;
 struct thr_info **mining_thr;
 struct thr_info **mining_thr;
 static int gwsched_thr_id;
 static int gwsched_thr_id;
-static int stage_thr_id;
 static int watchpool_thr_id;
 static int watchpool_thr_id;
 static int watchdog_thr_id;
 static int watchdog_thr_id;
 #ifdef HAVE_CURSES
 #ifdef HAVE_CURSES
@@ -1554,6 +1556,15 @@ static char *set_avalon_options(const char *arg)
 }
 }
 #endif
 #endif
 
 
+#ifdef USE_KLONDIKE
+static char *set_klondike_options(const char *arg)
+{
+	opt_set_charp(arg, &opt_klondike_options);
+
+	return NULL;
+}
+#endif
+
 __maybe_unused
 __maybe_unused
 static char *set_null(const char __maybe_unused *arg)
 static char *set_null(const char __maybe_unused *arg)
 {
 {
@@ -1808,6 +1819,11 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITH_ARG("--avalon-options",
 	OPT_WITH_ARG("--avalon-options",
 		     set_avalon_options, NULL, NULL,
 		     set_avalon_options, NULL, NULL,
 		     opt_hidden),
 		     opt_hidden),
+#endif
+#ifdef USE_KLONDIKE
+	OPT_WITH_ARG("--klondike-options",
+		     set_klondike_options, NULL, NULL,
+		     "Set klondike options clock:temptarget"),
 #endif
 #endif
 	OPT_WITHOUT_ARG("--load-balance",
 	OPT_WITHOUT_ARG("--load-balance",
 		     set_loadbalance, &pool_strategy,
 		     set_loadbalance, &pool_strategy,
@@ -3778,7 +3794,7 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work,
 			share_result_msg(work, "Accepted", "", resubmit, worktime);
 			share_result_msg(work, "Accepted", "", resubmit, worktime);
 		}
 		}
 		sharelog("accept", work);
 		sharelog("accept", work);
-		if (opt_shares && total_accepted >= opt_shares) {
+		if (opt_shares && total_diff_accepted >= opt_shares) {
 			applog(LOG_WARNING, "Successfully mined %d accepted shares as requested and exiting.", opt_shares);
 			applog(LOG_WARNING, "Successfully mined %d accepted shares as requested and exiting.", opt_shares);
 			kill_work();
 			kill_work();
 			return;
 			return;
@@ -3989,8 +4005,8 @@ static bool submit_upstream_work_completed(struct work *work, bool resubmit, str
 
 
 			snprintf(worktime, sizeof(worktime),
 			snprintf(worktime, sizeof(worktime),
 				" <-%08lx.%08lx M:%c D:%1.*f G:%02d:%02d:%02d:%1.3f %s (%1.3f) W:%1.3f (%1.3f) S:%1.3f R:%02d:%02d:%02d",
 				" <-%08lx.%08lx M:%c D:%1.*f G:%02d:%02d:%02d:%1.3f %s (%1.3f) W:%1.3f (%1.3f) S:%1.3f R:%02d:%02d:%02d",
-				(unsigned long)swab32(*(uint32_t *)&(work->data[opt_scrypt ? 32 : 28])),
-				(unsigned long)swab32(*(uint32_t *)&(work->data[opt_scrypt ? 28 : 24])),
+				(unsigned long)be32toh(*(uint32_t *)&(work->data[opt_scrypt ? 32 : 28])),
+				(unsigned long)be32toh(*(uint32_t *)&(work->data[opt_scrypt ? 28 : 24])),
 				work->getwork_mode, diffplaces, work->work_difficulty,
 				work->getwork_mode, diffplaces, work->work_difficulty,
 				tm_getwork.tm_hour, tm_getwork.tm_min,
 				tm_getwork.tm_hour, tm_getwork.tm_min,
 				tm_getwork.tm_sec, getwork_time, workclone,
 				tm_getwork.tm_sec, getwork_time, workclone,
@@ -4524,11 +4540,7 @@ static void __kill_work(void)
 		cgpu->status = LIFE_DEAD2;
 		cgpu->status = LIFE_DEAD2;
 	}
 	}
 
 
-	applog(LOG_DEBUG, "Killing off stage thread");
 	/* Stop the others */
 	/* Stop the others */
-	thr = &control_thr[stage_thr_id];
-	thr_info_cancel(thr);
-
 	applog(LOG_DEBUG, "Killing off API thread");
 	applog(LOG_DEBUG, "Killing off API thread");
 	thr = &control_thr[api_thr_id];
 	thr = &control_thr[api_thr_id];
 	thr_info_cancel(thr);
 	thr_info_cancel(thr);
@@ -4550,14 +4562,14 @@ const
 #endif
 #endif
 char **initial_args;
 char **initial_args;
 
 
-void _bfg_clean_up(void);
+void _bfg_clean_up(bool);
 
 
 void app_restart(void)
 void app_restart(void)
 {
 {
 	applog(LOG_WARNING, "Attempting to restart %s", packagename);
 	applog(LOG_WARNING, "Attempting to restart %s", packagename);
 
 
 	__kill_work();
 	__kill_work();
-	_bfg_clean_up();
+	_bfg_clean_up(true);
 
 
 #if defined(unix) || defined(__APPLE__)
 #if defined(unix) || defined(__APPLE__)
 	if (forkpid > 0) {
 	if (forkpid > 0) {
@@ -5586,8 +5598,11 @@ void switch_pools(struct pool *selected)
 static void discard_work(struct work *work)
 static void discard_work(struct work *work)
 {
 {
 	if (!work->clone && !work->rolls && !work->mined) {
 	if (!work->clone && !work->rolls && !work->mined) {
-		if (work->pool)
+		if (work->pool) {
 			work->pool->discarded_work++;
 			work->pool->discarded_work++;
+			work->pool->quota_used--;
+			work->pool->works--;
+		}
 		total_discarded++;
 		total_discarded++;
 		applog(LOG_DEBUG, "Discarded work");
 		applog(LOG_DEBUG, "Discarded work");
 	} else
 	} else
@@ -5892,45 +5907,6 @@ static bool hash_push(struct work *work)
 	return rc;
 	return rc;
 }
 }
 
 
-static void *stage_thread(void *userdata)
-{
-	struct thr_info *mythr = userdata;
-	bool ok = true;
-
-#ifndef HAVE_PTHREAD_CANCEL
-	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
-#endif
-
-	RenameThread("stage");
-
-	while (ok) {
-		struct work *work = NULL;
-
-		applog(LOG_DEBUG, "Popping work to stage thread");
-
-		work = tq_pop(mythr->q, NULL);
-		if (unlikely(!work)) {
-			applog(LOG_ERR, "Failed to tq_pop in stage_thread");
-			ok = false;
-			break;
-		}
-		work->work_restart_id = work->pool->work_restart_id;
-
-		test_work_current(work);
-
-		applog(LOG_DEBUG, "Pushing work %d to getwork queue (queued=%c)",
-		       work->id, work->queued?'Y':'N');
-
-		if (unlikely(!hash_push(work))) {
-			applog(LOG_WARNING, "Failed to hash_push in stage_thread");
-			continue;
-		}
-	}
-
-	tq_freeze(mythr->q);
-	return NULL;
-}
-
 static void stage_work(struct work *work)
 static void stage_work(struct work *work)
 {
 {
 	applog(LOG_DEBUG, "Pushing work %d from pool %d to hash queue",
 	applog(LOG_DEBUG, "Pushing work %d from pool %d to hash queue",
@@ -5939,6 +5915,7 @@ static void stage_work(struct work *work)
 	work->pool->last_work_time = time(NULL);
 	work->pool->last_work_time = time(NULL);
 	cgtime(&work->pool->tv_last_work_time);
 	cgtime(&work->pool->tv_last_work_time);
 	test_work_current(work);
 	test_work_current(work);
+	work->pool->works++;
 	hash_push(work);
 	hash_push(work);
 }
 }
 
 
@@ -5993,6 +5970,7 @@ static void display_pool_summary(struct pool *pool)
 		efficiency = pool_bytes_xfer ? pool->diff_accepted * 2048. / pool_bytes_xfer : 0.0;
 		efficiency = pool_bytes_xfer ? pool->diff_accepted * 2048. / pool_bytes_xfer : 0.0;
 		wlog(" Efficiency (accepted * difficulty / 2 KB): %.2f\n", efficiency);
 		wlog(" Efficiency (accepted * difficulty / 2 KB): %.2f\n", efficiency);
 
 
+		wlog(" Items worked on: %d\n", pool->works);
 		wlog(" Stale submissions discarded due to new blocks: %d\n", pool->stale_shares);
 		wlog(" Stale submissions discarded due to new blocks: %d\n", pool->stale_shares);
 		wlog(" Unable to get work from server occasions: %d\n", pool->getfail_occasions);
 		wlog(" Unable to get work from server occasions: %d\n", pool->getfail_occasions);
 		wlog(" Submitting work remotely delay occasions: %d\n\n", pool->remotefail_occasions);
 		wlog(" Submitting work remotely delay occasions: %d\n\n", pool->remotefail_occasions);
@@ -6357,6 +6335,10 @@ void write_config(FILE *fcfg)
 		fprintf(fcfg, ",\n\"icarus-options\" : \"%s\"", json_escape(opt_icarus_options));
 		fprintf(fcfg, ",\n\"icarus-options\" : \"%s\"", json_escape(opt_icarus_options));
 	if (opt_icarus_timing)
 	if (opt_icarus_timing)
 		fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", json_escape(opt_icarus_timing));
 		fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", json_escape(opt_icarus_timing));
+#ifdef USE_KLONDIKE
+	if (opt_klondike_options)
+		fprintf(fcfg, ",\n\"klondike-options\" : \"%s\"", json_escape(opt_icarus_options));
+#endif
 	fputs("\n}\n", fcfg);
 	fputs("\n}\n", fcfg);
 
 
 	json_escape_free();
 	json_escape_free();
@@ -7353,7 +7335,7 @@ static void hashmeter(int thr_id, struct timeval *diff,
 
 
 	local_secs = (double)total_diff.tv_sec + ((double)total_diff.tv_usec / 1000000.0);
 	local_secs = (double)total_diff.tv_sec + ((double)total_diff.tv_usec / 1000000.0);
 	decay_time(&total_rolling, local_mhashes_done / local_secs, local_secs);
 	decay_time(&total_rolling, local_mhashes_done / local_secs, local_secs);
-	global_hashrate = roundl(total_rolling) * 1000000;
+	global_hashrate = llround(total_rolling) * 1000000;
 
 
 	timersub(&total_tv_end, &total_tv_start, &total_diff);
 	timersub(&total_tv_end, &total_tv_start, &total_diff);
 	total_secs = (double)total_diff.tv_sec +
 	total_secs = (double)total_diff.tv_sec +
@@ -8095,7 +8077,7 @@ retry_stratum:
 
 
 			applog(LOG_DEBUG, "Pushing pooltest work to base pool");
 			applog(LOG_DEBUG, "Pushing pooltest work to base pool");
 
 
-			tq_push(control_thr[stage_thr_id].q, work);
+			stage_work(work);
 			total_getworks++;
 			total_getworks++;
 			pool->getwork_requested++;
 			pool->getwork_requested++;
 			ret = true;
 			ret = true;
@@ -8204,9 +8186,11 @@ retry:
 			no_work = true;
 			no_work = true;
 		}
 		}
 		ts = (struct timespec){ .tv_sec = opt_log_interval, };
 		ts = (struct timespec){ .tv_sec = opt_log_interval, };
+		pthread_cond_signal(&gws_cond);
 		if (ETIMEDOUT == pthread_cond_timedwait(&getq->cond, stgd_lock, &ts))
 		if (ETIMEDOUT == pthread_cond_timedwait(&getq->cond, stgd_lock, &ts))
 		{
 		{
 			run_cmd(cmd_idle);
 			run_cmd(cmd_idle);
+			pthread_cond_signal(&gws_cond);
 			pthread_cond_wait(&getq->cond, stgd_lock);
 			pthread_cond_wait(&getq->cond, stgd_lock);
 		}
 		}
 	}
 	}
@@ -8702,29 +8686,35 @@ void mt_disable_start(struct thr_info *mythr)
 	mythr->_mt_disable_called = true;
 	mythr->_mt_disable_called = true;
 }
 }
 
 
-/* Create a hashtable of work items for devices with a queue. The device
- * driver must have a custom queue_full function or it will default to true
- * and put only one work item in the queue. Work items should not be removed
- * from this hashtable until they are no longer in use anywhere. Once a work
- * item is physically queued on the device itself, the work->queued flag
- * should be set under cgpu->qlock write lock to prevent it being dereferenced
- * while still in use. */
+/* Put a new unqueued work item in cgpu->unqueued_work under cgpu->qlock till
+ * the driver tells us it's full so that it may extract the work item using
+ * the get_queued() function which adds it to the hashtable on
+ * cgpu->queued_work. */
 static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct device_drv *drv, const int thr_id)
 static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct device_drv *drv, const int thr_id)
 {
 {
 	thread_reportout(mythr);
 	thread_reportout(mythr);
 	do {
 	do {
 		bool need_work;
 		bool need_work;
 
 
-		rd_lock(&cgpu->qlock);
-		need_work = (HASH_COUNT(cgpu->queued_work) == cgpu->queued_count);
-		rd_unlock(&cgpu->qlock);
+		/* Do this lockless just to know if we need more unqueued work. */
+		need_work = (!cgpu->unqueued_work);
 
 
+		/* get_work is a blocking function so do it outside of lock
+		 * to prevent deadlocks with other locks. */
 		if (need_work) {
 		if (need_work) {
 			struct work *work = get_work(mythr);
 			struct work *work = get_work(mythr);
 
 
 			wr_lock(&cgpu->qlock);
 			wr_lock(&cgpu->qlock);
-			HASH_ADD_INT(cgpu->queued_work, id, work);
+			/* Check we haven't grabbed work somehow between
+			 * checking and picking up the lock. */
+			if (likely(!cgpu->unqueued_work))
+				cgpu->unqueued_work = work;
+			else
+				need_work = false;
 			wr_unlock(&cgpu->qlock);
 			wr_unlock(&cgpu->qlock);
+
+			if (unlikely(!need_work))
+				discard_work(work);
 		}
 		}
 		/* The queue_full function should be used by the driver to
 		/* The queue_full function should be used by the driver to
 		 * actually place work items on the physical device if it
 		 * actually place work items on the physical device if it
@@ -8732,26 +8722,45 @@ static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct de
 	} while (drv->queue_full && !drv->queue_full(cgpu));
 	} while (drv->queue_full && !drv->queue_full(cgpu));
 }
 }
 
 
-/* This function is for retrieving one work item from the queued hashtable of
- * available work items that are not yet physically on a device (which is
- * flagged with the work->queued bool). Code using this function must be able
- * to handle NULL as a return which implies there is no work available. */
+/* Add a work item to a cgpu's queued hashlist */
+void __add_queued(struct cgpu_info *cgpu, struct work *work)
+{
+	cgpu->queued_count++;
+	HASH_ADD_INT(cgpu->queued_work, id, work);
+}
+
+/* This function is for retrieving one work item from the unqueued pointer and
+ * adding it to the hashtable of queued work. Code using this function must be
+ * able to handle NULL as a return which implies there is no work available. */
 struct work *get_queued(struct cgpu_info *cgpu)
 struct work *get_queued(struct cgpu_info *cgpu)
 {
 {
-	struct work *work, *tmp, *ret = NULL;
+	struct work *work = NULL;
 
 
 	wr_lock(&cgpu->qlock);
 	wr_lock(&cgpu->qlock);
-	HASH_ITER(hh, cgpu->queued_work, work, tmp) {
-		if (!work->queued) {
-			work->queued = true;
-			cgpu->queued_count++;
-			ret = work;
-			break;
-		}
+	if (cgpu->unqueued_work) {
+		work = cgpu->unqueued_work;
+		__add_queued(cgpu, work);
+		cgpu->unqueued_work = NULL;
 	}
 	}
 	wr_unlock(&cgpu->qlock);
 	wr_unlock(&cgpu->qlock);
 
 
-	return ret;
+	return work;
+}
+
+void add_queued(struct cgpu_info *cgpu, struct work *work)
+{
+	wr_lock(&cgpu->qlock);
+	__add_queued(cgpu, work);
+	wr_unlock(&cgpu->qlock);
+}
+
+/* Get fresh work and add it to cgpu's queued hashlist */
+struct work *get_queue_work(struct thr_info *thr, struct cgpu_info *cgpu, int thr_id)
+{
+	struct work *work = get_work(thr);
+
+	add_queued(cgpu, work);
+	return work;
 }
 }
 
 
 /* This function is for finding an already queued work item in the
 /* This function is for finding an already queued work item in the
@@ -8764,8 +8773,7 @@ struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t mid
 	struct work *work, *tmp, *ret = NULL;
 	struct work *work, *tmp, *ret = NULL;
 
 
 	HASH_ITER(hh, que, work, tmp) {
 	HASH_ITER(hh, que, work, tmp) {
-		if (work->queued &&
-		    memcmp(work->midstate, midstate, midstatelen) == 0 &&
+		if (memcmp(work->midstate, midstate, midstatelen) == 0 &&
 		    memcmp(work->data + offset, data, datalen) == 0) {
 		    memcmp(work->data + offset, data, datalen) == 0) {
 			ret = work;
 			ret = work;
 			break;
 			break;
@@ -8803,10 +8811,9 @@ struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate
 	return ret;
 	return ret;
 }
 }
 
 
-static void __work_completed(struct cgpu_info *cgpu, struct work *work)
+void __work_completed(struct cgpu_info *cgpu, struct work *work)
 {
 {
-	if (work->queued)
-		cgpu->queued_count--;
+	cgpu->queued_count--;
 	HASH_DEL(cgpu->queued_work, work);
 	HASH_DEL(cgpu->queued_work, work);
 }
 }
 /* This function should be used by queued device drivers when they're sure
 /* This function should be used by queued device drivers when they're sure
@@ -8837,23 +8844,17 @@ struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate,
 
 
 static void flush_queue(struct cgpu_info *cgpu)
 static void flush_queue(struct cgpu_info *cgpu)
 {
 {
-	struct work *work, *tmp;
-	int discarded = 0;
+	struct work *work = NULL;
 
 
 	wr_lock(&cgpu->qlock);
 	wr_lock(&cgpu->qlock);
-	HASH_ITER(hh, cgpu->queued_work, work, tmp) {
-		/* Can only discard the work items if they're not physically
-		 * queued on the device. */
-		if (!work->queued) {
-			HASH_DEL(cgpu->queued_work, work);
-			discard_work(work);
-			discarded++;
-		}
-	}
+	work = cgpu->unqueued_work;
+	cgpu->unqueued_work = NULL;
 	wr_unlock(&cgpu->qlock);
 	wr_unlock(&cgpu->qlock);
 
 
-	if (discarded)
-		applog(LOG_DEBUG, "Discarded %d queued work items", discarded);
+	if (work) {
+		free_work(work);
+		applog(LOG_DEBUG, "Discarded queued work item");
+	}
 }
 }
 
 
 /* This version of hash work is for devices that are fast enough to always
 /* This version of hash work is for devices that are fast enough to always
@@ -8876,12 +8877,15 @@ void hash_queued_work(struct thr_info *mythr)
 		struct timeval diff;
 		struct timeval diff;
 		int64_t hashes;
 		int64_t hashes;
 
 
-		mythr->work_restart = false;
-
 		fill_queue(mythr, cgpu, drv, thr_id);
 		fill_queue(mythr, cgpu, drv, thr_id);
 
 
 		thread_reportin(mythr);
 		thread_reportin(mythr);
 		hashes = drv->scanwork(mythr);
 		hashes = drv->scanwork(mythr);
+
+		/* Reset the bool here in case the driver looks for it
+		 * synchronously in the scanwork loop. */
+		mythr->work_restart = false;
+
 		if (unlikely(hashes == -1 )) {
 		if (unlikely(hashes == -1 )) {
 			applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id);
 			applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id);
 			cgpu->deven = DEV_DISABLED;
 			cgpu->deven = DEV_DISABLED;
@@ -9714,6 +9718,7 @@ void print_summary(void)
 			efficiency = pool_bytes_xfer ? pool->diff_accepted * 2048. / pool_bytes_xfer : 0.0;
 			efficiency = pool_bytes_xfer ? pool->diff_accepted * 2048. / pool_bytes_xfer : 0.0;
 			applog(LOG_WARNING, " Efficiency (accepted * difficulty / 2 KB): %.2f", efficiency);
 			applog(LOG_WARNING, " Efficiency (accepted * difficulty / 2 KB): %.2f", efficiency);
 
 
+			applog(LOG_WARNING, " Items worked on: %d", pool->works);
 			applog(LOG_WARNING, " Unable to get work from server occasions: %d", pool->getfail_occasions);
 			applog(LOG_WARNING, " Unable to get work from server occasions: %d", pool->getfail_occasions);
 			applog(LOG_WARNING, " Submitting work remotely delay occasions: %d\n", pool->remotefail_occasions);
 			applog(LOG_WARNING, " Submitting work remotely delay occasions: %d\n", pool->remotefail_occasions);
 		}
 		}
@@ -9734,9 +9739,9 @@ void print_summary(void)
 	}
 	}
 
 
 	if (opt_shares) {
 	if (opt_shares) {
-		applog(LOG_WARNING, "Mined %d accepted shares of %d requested\n", total_accepted, opt_shares);
-		if (opt_shares > total_accepted)
-			applog(LOG_WARNING, "WARNING - Mined only %d shares of %d requested.", total_accepted, opt_shares);
+		applog(LOG_WARNING, "Mined %.0f accepted shares of %d requested\n", total_diff_accepted, opt_shares);
+		if (opt_shares > total_diff_accepted)
+			applog(LOG_WARNING, "WARNING - Mined only %.0f shares of %d requested.", total_diff_accepted, opt_shares);
 	}
 	}
 	applog(LOG_WARNING, " ");
 	applog(LOG_WARNING, " ");
 
 
@@ -9744,7 +9749,7 @@ void print_summary(void)
 	fflush(stdout);
 	fflush(stdout);
 }
 }
 
 
-void _bfg_clean_up(void)
+void _bfg_clean_up(bool restarting)
 {
 {
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
 	clear_adl(nDevs);
 	clear_adl(nDevs);
@@ -9758,11 +9763,15 @@ void _bfg_clean_up(void)
 #ifdef WIN32
 #ifdef WIN32
 	timeEndPeriod(1);
 	timeEndPeriod(1);
 #endif
 #endif
+	if (!restarting) {
+		/* Attempting to disable curses or print a summary during a
+		 * restart can lead to a deadlock. */
 #ifdef HAVE_CURSES
 #ifdef HAVE_CURSES
-	disable_curses();
+		disable_curses();
 #endif
 #endif
-	if (!opt_realquiet && successful_connect)
-		print_summary();
+		if (!opt_realquiet && successful_connect)
+			print_summary();
+	}
 
 
 	if (opt_n_threads)
 	if (opt_n_threads)
 		free(cpus);
 		free(cpus);
@@ -10800,22 +10809,12 @@ int main(int argc, char *argv[])
 			quit(1, "Failed to calloc mining_thr[%d]", i);
 			quit(1, "Failed to calloc mining_thr[%d]", i);
 	}
 	}
 
 
-	total_control_threads = 7;
+	total_control_threads = 6;
 	control_thr = calloc(total_control_threads, sizeof(*thr));
 	control_thr = calloc(total_control_threads, sizeof(*thr));
 	if (!control_thr)
 	if (!control_thr)
 		quit(1, "Failed to calloc control_thr");
 		quit(1, "Failed to calloc control_thr");
 
 
 	gwsched_thr_id = 0;
 	gwsched_thr_id = 0;
-	stage_thr_id = 1;
-	thr = &control_thr[stage_thr_id];
-	thr->q = tq_new();
-	if (!thr->q)
-		quit(1, "Failed to tq_new");
-	/* start stage thread */
-	if (thr_info_create(thr, NULL, stage_thread, thr))
-		quit(1, "stage thread create failed");
-	pthread_detach(thr->pth);
-
 	/* Create a unique get work queue */
 	/* Create a unique get work queue */
 	getq = tq_new();
 	getq = tq_new();
 	if (!getq)
 	if (!getq)
@@ -10934,14 +10933,14 @@ begin_bench:
 			quit(1, "submit_work thread create failed");
 			quit(1, "submit_work thread create failed");
 	}
 	}
 
 
-	watchpool_thr_id = 2;
+	watchpool_thr_id = 1;
 	thr = &control_thr[watchpool_thr_id];
 	thr = &control_thr[watchpool_thr_id];
 	/* start watchpool thread */
 	/* start watchpool thread */
 	if (thr_info_create(thr, NULL, watchpool_thread, NULL))
 	if (thr_info_create(thr, NULL, watchpool_thread, NULL))
 		quit(1, "watchpool thread create failed");
 		quit(1, "watchpool thread create failed");
 	pthread_detach(thr->pth);
 	pthread_detach(thr->pth);
 
 
-	watchdog_thr_id = 3;
+	watchdog_thr_id = 2;
 	thr = &control_thr[watchdog_thr_id];
 	thr = &control_thr[watchdog_thr_id];
 	/* start watchdog thread */
 	/* start watchdog thread */
 	if (thr_info_create(thr, NULL, watchdog_thread, NULL))
 	if (thr_info_create(thr, NULL, watchdog_thread, NULL))
@@ -10950,7 +10949,7 @@ begin_bench:
 
 
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
 	/* Create reinit gpu thread */
 	/* Create reinit gpu thread */
-	gpur_thr_id = 4;
+	gpur_thr_id = 3;
 	thr = &control_thr[gpur_thr_id];
 	thr = &control_thr[gpur_thr_id];
 	thr->q = tq_new();
 	thr->q = tq_new();
 	if (!thr->q)
 	if (!thr->q)
@@ -10960,7 +10959,7 @@ begin_bench:
 #endif	
 #endif	
 
 
 	/* Create API socket thread */
 	/* Create API socket thread */
-	api_thr_id = 5;
+	api_thr_id = 4;
 	thr = &control_thr[api_thr_id];
 	thr = &control_thr[api_thr_id];
 	if (thr_info_create(thr, NULL, api_thread, thr))
 	if (thr_info_create(thr, NULL, api_thread, thr))
 		quit(1, "API thread create failed");
 		quit(1, "API thread create failed");
@@ -10979,7 +10978,7 @@ begin_bench:
 	/* Create curses input thread for keyboard input. Create this last so
 	/* Create curses input thread for keyboard input. Create this last so
 	 * that we know all threads are created since this can call kill_work
 	 * that we know all threads are created since this can call kill_work
 	 * to try and shut down ll previous threads. */
 	 * to try and shut down ll previous threads. */
-	input_thr_id = 6;
+	input_thr_id = 5;
 	thr = &control_thr[input_thr_id];
 	thr = &control_thr[input_thr_id];
 	if (thr_info_create(thr, NULL, input_thread, thr))
 	if (thr_info_create(thr, NULL, input_thread, thr))
 		quit(1, "input thread create failed");
 		quit(1, "input thread create failed");
@@ -10987,7 +10986,7 @@ begin_bench:
 #endif
 #endif
 
 
 	/* Just to be sure */
 	/* Just to be sure */
-	if (total_control_threads != 7)
+	if (total_control_threads != 6)
 		quit(1, "incorrect total_control_threads (%d) should be 7", total_control_threads);
 		quit(1, "incorrect total_control_threads (%d) should be 7", total_control_threads);
 
 
 	/* Once everything is set up, main() becomes the getwork scheduler */
 	/* Once everything is set up, main() becomes the getwork scheduler */

+ 34 - 5
miner.h

@@ -213,10 +213,6 @@ static inline int fsync (int fd)
 #	endif
 #	endif
 #endif
 #endif
 
 
-#ifdef NEED_ROUNDL
-#define roundl(x)   (long double)((long long)((x==0)?0.0:((x)+(((x)>0)?0.5:-0.5))))
-#endif
-
 enum alive {
 enum alive {
 	LIFE_WELL,
 	LIFE_WELL,
 	LIFE_SICK,
 	LIFE_SICK,
@@ -584,6 +580,7 @@ struct cgpu_info {
 
 
 	pthread_rwlock_t qlock;
 	pthread_rwlock_t qlock;
 	struct work *queued_work;
 	struct work *queued_work;
+	struct work *unqueued_work;
 	unsigned int queued_count;
 	unsigned int queued_count;
 
 
 	bool disable_watchdog;
 	bool disable_watchdog;
@@ -822,6 +819,13 @@ static inline void mutex_init(pthread_mutex_t *lock)
 		quit(1, "Failed to pthread_mutex_init");
 		quit(1, "Failed to pthread_mutex_init");
 }
 }
 
 
+static inline void mutex_destroy(pthread_mutex_t *lock)
+{
+	/* Ignore return code. This only invalidates the mutex on linux but
+	 * releases resources on windows. */
+	pthread_mutex_destroy(lock);
+}
+
 static inline void rwlock_init(pthread_rwlock_t *lock)
 static inline void rwlock_init(pthread_rwlock_t *lock)
 {
 {
 	if (unlikely(pthread_rwlock_init(lock, NULL)))
 	if (unlikely(pthread_rwlock_init(lock, NULL)))
@@ -836,12 +840,23 @@ struct cglock {
 
 
 typedef struct cglock cglock_t;
 typedef struct cglock cglock_t;
 
 
+static inline void rwlock_destroy(pthread_rwlock_t *lock)
+{
+	pthread_rwlock_destroy(lock);
+}
+
 static inline void cglock_init(cglock_t *lock)
 static inline void cglock_init(cglock_t *lock)
 {
 {
 	mutex_init(&lock->mutex);
 	mutex_init(&lock->mutex);
 	rwlock_init(&lock->rwlock);
 	rwlock_init(&lock->rwlock);
 }
 }
 
 
+static inline void cglock_destroy(cglock_t *lock)
+{
+	rwlock_destroy(&lock->rwlock);
+	mutex_destroy(&lock->mutex);
+}
+
 /* Read lock variant of cglock. Cannot be promoted. */
 /* Read lock variant of cglock. Cannot be promoted. */
 static inline void cg_rlock(cglock_t *lock)
 static inline void cg_rlock(cglock_t *lock)
 {
 {
@@ -945,6 +960,9 @@ extern bool opt_worktime;
 #ifdef USE_AVALON
 #ifdef USE_AVALON
 extern char *opt_avalon_options;
 extern char *opt_avalon_options;
 #endif
 #endif
+#ifdef USE_KLONDIKE
+extern char *opt_klondike_options;
+#endif
 #ifdef USE_BITFORCE
 #ifdef USE_BITFORCE
 extern bool opt_bfl_noncerange;
 extern bool opt_bfl_noncerange;
 #endif
 #endif
@@ -1209,6 +1227,7 @@ struct pool {
 	int quota;
 	int quota;
 	int quota_gcd;
 	int quota_gcd;
 	int quota_used;
 	int quota_used;
+	int works;
 
 
 	double diff_accepted;
 	double diff_accepted;
 	double diff_rejected;
 	double diff_rejected;
@@ -1335,7 +1354,6 @@ struct work {
 	bool		stale;
 	bool		stale;
 	bool		mandatory;
 	bool		mandatory;
 	bool		block;
 	bool		block;
-	bool		queued;
 
 
 	bool		stratum;
 	bool		stratum;
 	char 		*job_id;
 	char 		*job_id;
@@ -1353,6 +1371,9 @@ struct work {
 	// Allow devices to identify work if multiple sub-devices
 	// Allow devices to identify work if multiple sub-devices
 	// DEPRECATED: New code should be using multiple processors instead
 	// DEPRECATED: New code should be using multiple processors instead
 	unsigned char	subid;
 	unsigned char	subid;
+	
+	// Allow devices to timestamp work for their own purposes
+	struct timeval	tv_stamp;
 
 
 	blktemplate_t	*tmpl;
 	blktemplate_t	*tmpl;
 	int		*tmpl_refcount;
 	int		*tmpl_refcount;
@@ -1389,10 +1410,14 @@ extern enum test_nonce2_result _test_nonce2(struct work *, uint32_t nonce, bool
 #define test_nonce(work, nonce, checktarget)  (_test_nonce2(work, nonce, checktarget) == TNR_GOOD)
 #define test_nonce(work, nonce, checktarget)  (_test_nonce2(work, nonce, checktarget) == TNR_GOOD)
 #define test_nonce2(work, nonce)  (_test_nonce2(work, nonce, true))
 #define test_nonce2(work, nonce)  (_test_nonce2(work, nonce, true))
 extern bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce);
 extern bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce);
+extern void __add_queued(struct cgpu_info *cgpu, struct work *work);
 extern struct work *get_queued(struct cgpu_info *cgpu);
 extern struct work *get_queued(struct cgpu_info *cgpu);
+extern void add_queued(struct cgpu_info *cgpu, struct work *work);
+extern struct work *get_queue_work(struct thr_info *thr, struct cgpu_info *cgpu, int thr_id);
 extern struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
+extern void __work_completed(struct cgpu_info *cgpu, struct work *work);
 extern void work_completed(struct cgpu_info *cgpu, struct work *work);
 extern void work_completed(struct cgpu_info *cgpu, struct work *work);
 extern struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern bool abandon_work(struct work *, struct timeval *work_runtime, uint64_t hashes);
 extern bool abandon_work(struct work *, struct timeval *work_runtime, uint64_t hashes);
@@ -1441,6 +1466,8 @@ enum api_data_type {
 	API_ESCAPE,
 	API_ESCAPE,
 	API_STRING,
 	API_STRING,
 	API_CONST,
 	API_CONST,
+	API_UINT8,
+	API_UINT16,
 	API_INT,
 	API_INT,
 	API_UINT,
 	API_UINT,
 	API_UINT32,
 	API_UINT32,
@@ -1474,6 +1501,8 @@ struct api_data {
 extern struct api_data *api_add_escape(struct api_data *root, char *name, char *data, bool copy_data);
 extern struct api_data *api_add_escape(struct api_data *root, char *name, char *data, bool copy_data);
 extern struct api_data *api_add_string(struct api_data *root, char *name, const char *data, bool copy_data);
 extern struct api_data *api_add_string(struct api_data *root, char *name, const char *data, bool copy_data);
 extern struct api_data *api_add_const(struct api_data *root, char *name, const char *data, bool copy_data);
 extern struct api_data *api_add_const(struct api_data *root, char *name, const char *data, bool copy_data);
+extern struct api_data *api_add_uint8(struct api_data *root, char *name, uint8_t *data, bool copy_data);
+extern struct api_data *api_add_uint16(struct api_data *root, char *name, uint16_t *data, bool copy_data);
 extern struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool copy_data);
 extern struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool copy_data);
 extern struct api_data *api_add_uint(struct api_data *root, char *name, unsigned int *data, bool copy_data);
 extern struct api_data *api_add_uint(struct api_data *root, char *name, unsigned int *data, bool copy_data);
 extern struct api_data *api_add_uint32(struct api_data *root, char *name, uint32_t *data, bool copy_data);
 extern struct api_data *api_add_uint32(struct api_data *root, char *name, uint32_t *data, bool copy_data);

+ 0 - 10
sha2.c

@@ -39,16 +39,6 @@
 
 
 #include "sha2.h"
 #include "sha2.h"
 
 
-#define SHFR(x, n)    (x >> n)
-#define ROTR(x, n)   ((x >> n) | (x << ((sizeof(x) << 3) - n)))
-#define CH(x, y, z)  ((x & y) ^ (~x & z))
-#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
-
-#define SHA256_F1(x) (ROTR(x,  2) ^ ROTR(x, 13) ^ ROTR(x, 22))
-#define SHA256_F2(x) (ROTR(x,  6) ^ ROTR(x, 11) ^ ROTR(x, 25))
-#define SHA256_F3(x) (ROTR(x,  7) ^ ROTR(x, 18) ^ SHFR(x,  3))
-#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
-
 #define UNPACK32(x, str)                      \
 #define UNPACK32(x, str)                      \
 {                                             \
 {                                             \
     *((str) + 3) = (uint8_t) ((x)      );       \
     *((str) + 3) = (uint8_t) ((x)      );       \

+ 12 - 0
sha2.h

@@ -44,6 +44,16 @@
 #define SHA256_DIGEST_SIZE ( 256 / 8)
 #define SHA256_DIGEST_SIZE ( 256 / 8)
 #define SHA256_BLOCK_SIZE  ( 512 / 8)
 #define SHA256_BLOCK_SIZE  ( 512 / 8)
 
 
+#define SHFR(x, n)    (x >> n)
+#define ROTR(x, n)   ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define CH(x, y, z)  ((x & y) ^ (~x & z))
+#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+
+#define SHA256_F1(x) (ROTR(x,  2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define SHA256_F2(x) (ROTR(x,  6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define SHA256_F3(x) (ROTR(x,  7) ^ ROTR(x, 18) ^ SHFR(x,  3))
+#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
+
 typedef struct {
 typedef struct {
     unsigned int tot_len;
     unsigned int tot_len;
     unsigned int len;
     unsigned int len;
@@ -51,6 +61,8 @@ typedef struct {
     uint32_t h[8];
     uint32_t h[8];
 } sha256_ctx;
 } sha256_ctx;
 
 
+extern uint32_t sha256_k[64];
+
 void sha256_init(sha256_ctx * ctx);
 void sha256_init(sha256_ctx * ctx);
 void sha256_update(sha256_ctx *ctx, const unsigned char *message,
 void sha256_update(sha256_ctx *ctx, const unsigned char *message,
                    unsigned int len);
                    unsigned int len);

+ 2 - 0
util.h

@@ -362,6 +362,8 @@ long timer_elapsed_us(const struct timeval *tvp_timer, const struct timeval *tvp
 	return timeval_to_us(&tv);
 	return timeval_to_us(&tv);
 }
 }
 
 
+#define ms_tdiff(end, start)  (timer_elapsed_us(start, end) / 1000)
+
 static inline
 static inline
 int timer_elapsed(const struct timeval *tvp_timer, const struct timeval *tvp_now)
 int timer_elapsed(const struct timeval *tvp_timer, const struct timeval *tvp_now)
 {
 {