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
 
 
-# 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
-
-# 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 += adl.c adl.h adl_functions.h
 bfgminer_SOURCES += *.cl
@@ -228,6 +223,10 @@ if USE_KNC
 bfgminer_SOURCES += driver-knc.c
 endif
 
+if HAS_KLONDIKE
+bfgminer_SOURCES += driver-klondike.c
+endif
+
 if HAS_MODMINER
 bfgminer_SOURCES += driver-modminer.c
 endif

+ 21 - 20
README

@@ -38,15 +38,6 @@ MORE INFORMATION ON EACH.
 
 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:
 
 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
 
-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
 
-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
 
@@ -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
 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
 
@@ -115,7 +116,7 @@ Optional Dependencies:
 	Stratum proxy:
 	  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/
 
 	Video card GPU mining (free):
@@ -145,6 +146,7 @@ BFGMiner specific configuration options:
 	--enable-metabank       Compile support for Metabank (default disabled)
 	--disable-bitforce      Compile support for BitForce (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)
 	--disable-modminer      Compile support for ModMiner (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: . [-atDdGCgIKklmpPQqrRsTouvwOchnV] 
+Usage: bfgminer [-DdElmpPQqUsTouOchnV]
+
 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]
                     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?
 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
-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
 option --failover-only.
 
@@ -725,8 +728,7 @@ other factors.
 
 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
-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?
 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.
 
 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?
 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).
 
 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?
 A: On Linux, if the /dev/ttyUSB* devices don't automatically appear, the only

+ 8 - 2
README.ASIC

@@ -1,8 +1,8 @@
 SUPPORTED DEVICES
 
 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
@@ -121,6 +121,12 @@ ports. They communicate with the Icarus protocol, which has some additional
 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

+ 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:
 
 
+API V2.2
+
+Modified API command:
+ 'pools' - add 'Works'
+
+---------
+
 API V2.1 (BFGMiner v3.4.0)
 
 Added API command:

+ 28 - 1
api.c

@@ -60,7 +60,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 
-static const char *APIVERSION = "2.1";
+static const char *APIVERSION = "2.2";
 static const char *DEAD = "Dead";
 static const char *SICK = "Sick";
 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);
 				strcpy((char*)(api_data->data), (char *)data);
 				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:
 				api_data->data = (void *)malloc(sizeof(int));
 				*((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);
 }
 
+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)
 {
 	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)
 					free(escape);
 				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:
 				sprintf(buf, "%d", *((int *)(root->data)));
 				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_int(root, "Accepted", &(pool->accepted), 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, "Stale", &(pool->stale_shares), 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])
 
 driverlist="$driverlist opencl"
-opencl="yes"
+opencl="no"
 
 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]
 	)
 if test "x$opencl" = xyes; then
@@ -384,6 +384,7 @@ if test "x$avalon" = xyes; then
 fi
 AM_CONDITIONAL([HAS_AVALON], [test x$avalon = xyes])
 
+
 driverlist="$driverlist knc"
 AC_ARG_ENABLE([knc],
 	[AC_HELP_STRING([--enable-knc],[Compile support for KnC (default disabled)])],
@@ -510,6 +511,28 @@ PKG_CHECK_MODULES([LIBUSB], [libusb-1.0],[
 	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"
 AC_ARG_ENABLE([x6500],
 	[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])
 
 
-if test "x$x6500$ztex" = "xnono"; then
+if test "x$klondike$x6500$ztex" = "xnonono"; then
 	libusb=no
 	LIBUSB_LIBS=''
 	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],
                                     [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
 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;  \
 } while (0)
 
-extern void _bfg_clean_up(void);
+extern void _bfg_clean_up(bool);
 
 #define quit(status, fmt, ...) do { \
-	_bfg_clean_up();  \
+	_bfg_clean_up(false);  \
 	if (fmt) { \
 		fprintf(stderr, fmt, ##__VA_ARGS__);  \
 	} \

+ 119 - 120
miner.c

@@ -222,6 +222,9 @@ bool opt_worktime;
 #ifdef USE_AVALON
 char *opt_avalon_options = NULL;
 #endif
+#ifdef USE_KLONDIKE
+char *opt_klondike_options = NULL;
+#endif
 
 char *opt_kernel_path;
 char *cgminer_path;
@@ -234,7 +237,6 @@ bool opt_bfl_noncerange;
 struct thr_info *control_thr;
 struct thr_info **mining_thr;
 static int gwsched_thr_id;
-static int stage_thr_id;
 static int watchpool_thr_id;
 static int watchdog_thr_id;
 #ifdef HAVE_CURSES
@@ -1554,6 +1556,15 @@ static char *set_avalon_options(const char *arg)
 }
 #endif
 
+#ifdef USE_KLONDIKE
+static char *set_klondike_options(const char *arg)
+{
+	opt_set_charp(arg, &opt_klondike_options);
+
+	return NULL;
+}
+#endif
+
 __maybe_unused
 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",
 		     set_avalon_options, NULL, NULL,
 		     opt_hidden),
+#endif
+#ifdef USE_KLONDIKE
+	OPT_WITH_ARG("--klondike-options",
+		     set_klondike_options, NULL, NULL,
+		     "Set klondike options clock:temptarget"),
 #endif
 	OPT_WITHOUT_ARG("--load-balance",
 		     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);
 		}
 		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);
 			kill_work();
 			return;
@@ -3989,8 +4005,8 @@ static bool submit_upstream_work_completed(struct work *work, bool resubmit, str
 
 			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",
-				(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,
 				tm_getwork.tm_hour, tm_getwork.tm_min,
 				tm_getwork.tm_sec, getwork_time, workclone,
@@ -4524,11 +4540,7 @@ static void __kill_work(void)
 		cgpu->status = LIFE_DEAD2;
 	}
 
-	applog(LOG_DEBUG, "Killing off stage thread");
 	/* Stop the others */
-	thr = &control_thr[stage_thr_id];
-	thr_info_cancel(thr);
-
 	applog(LOG_DEBUG, "Killing off API thread");
 	thr = &control_thr[api_thr_id];
 	thr_info_cancel(thr);
@@ -4550,14 +4562,14 @@ const
 #endif
 char **initial_args;
 
-void _bfg_clean_up(void);
+void _bfg_clean_up(bool);
 
 void app_restart(void)
 {
 	applog(LOG_WARNING, "Attempting to restart %s", packagename);
 
 	__kill_work();
-	_bfg_clean_up();
+	_bfg_clean_up(true);
 
 #if defined(unix) || defined(__APPLE__)
 	if (forkpid > 0) {
@@ -5586,8 +5598,11 @@ void switch_pools(struct pool *selected)
 static void discard_work(struct work *work)
 {
 	if (!work->clone && !work->rolls && !work->mined) {
-		if (work->pool)
+		if (work->pool) {
 			work->pool->discarded_work++;
+			work->pool->quota_used--;
+			work->pool->works--;
+		}
 		total_discarded++;
 		applog(LOG_DEBUG, "Discarded work");
 	} else
@@ -5892,45 +5907,6 @@ static bool hash_push(struct work *work)
 	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)
 {
 	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);
 	cgtime(&work->pool->tv_last_work_time);
 	test_work_current(work);
+	work->pool->works++;
 	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;
 		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(" Unable to get work from server occasions: %d\n", pool->getfail_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));
 	if (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);
 
 	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);
 	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);
 	total_secs = (double)total_diff.tv_sec +
@@ -8095,7 +8077,7 @@ retry_stratum:
 
 			applog(LOG_DEBUG, "Pushing pooltest work to base pool");
 
-			tq_push(control_thr[stage_thr_id].q, work);
+			stage_work(work);
 			total_getworks++;
 			pool->getwork_requested++;
 			ret = true;
@@ -8204,9 +8186,11 @@ retry:
 			no_work = true;
 		}
 		ts = (struct timespec){ .tv_sec = opt_log_interval, };
+		pthread_cond_signal(&gws_cond);
 		if (ETIMEDOUT == pthread_cond_timedwait(&getq->cond, stgd_lock, &ts))
 		{
 			run_cmd(cmd_idle);
+			pthread_cond_signal(&gws_cond);
 			pthread_cond_wait(&getq->cond, stgd_lock);
 		}
 	}
@@ -8702,29 +8686,35 @@ void mt_disable_start(struct thr_info *mythr)
 	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)
 {
 	thread_reportout(mythr);
 	do {
 		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) {
 			struct work *work = get_work(mythr);
 
 			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);
+
+			if (unlikely(!need_work))
+				discard_work(work);
 		}
 		/* The queue_full function should be used by the driver to
 		 * 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));
 }
 
-/* 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 *work, *tmp, *ret = NULL;
+	struct work *work = NULL;
 
 	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);
 
-	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
@@ -8764,8 +8773,7 @@ struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t mid
 	struct work *work, *tmp, *ret = NULL;
 
 	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) {
 			ret = work;
 			break;
@@ -8803,10 +8811,9 @@ struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate
 	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);
 }
 /* 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)
 {
-	struct work *work, *tmp;
-	int discarded = 0;
+	struct work *work = NULL;
 
 	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);
 
-	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
@@ -8876,12 +8877,15 @@ void hash_queued_work(struct thr_info *mythr)
 		struct timeval diff;
 		int64_t hashes;
 
-		mythr->work_restart = false;
-
 		fill_queue(mythr, cgpu, drv, thr_id);
 
 		thread_reportin(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 )) {
 			applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id);
 			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;
 			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, " Submitting work remotely delay occasions: %d\n", pool->remotefail_occasions);
 		}
@@ -9734,9 +9739,9 @@ void print_summary(void)
 	}
 
 	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, " ");
 
@@ -9744,7 +9749,7 @@ void print_summary(void)
 	fflush(stdout);
 }
 
-void _bfg_clean_up(void)
+void _bfg_clean_up(bool restarting)
 {
 #ifdef HAVE_OPENCL
 	clear_adl(nDevs);
@@ -9758,11 +9763,15 @@ void _bfg_clean_up(void)
 #ifdef WIN32
 	timeEndPeriod(1);
 #endif
+	if (!restarting) {
+		/* Attempting to disable curses or print a summary during a
+		 * restart can lead to a deadlock. */
 #ifdef HAVE_CURSES
-	disable_curses();
+		disable_curses();
 #endif
-	if (!opt_realquiet && successful_connect)
-		print_summary();
+		if (!opt_realquiet && successful_connect)
+			print_summary();
+	}
 
 	if (opt_n_threads)
 		free(cpus);
@@ -10800,22 +10809,12 @@ int main(int argc, char *argv[])
 			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));
 	if (!control_thr)
 		quit(1, "Failed to calloc control_thr");
 
 	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 */
 	getq = tq_new();
 	if (!getq)
@@ -10934,14 +10933,14 @@ begin_bench:
 			quit(1, "submit_work thread create failed");
 	}
 
-	watchpool_thr_id = 2;
+	watchpool_thr_id = 1;
 	thr = &control_thr[watchpool_thr_id];
 	/* start watchpool thread */
 	if (thr_info_create(thr, NULL, watchpool_thread, NULL))
 		quit(1, "watchpool thread create failed");
 	pthread_detach(thr->pth);
 
-	watchdog_thr_id = 3;
+	watchdog_thr_id = 2;
 	thr = &control_thr[watchdog_thr_id];
 	/* start watchdog thread */
 	if (thr_info_create(thr, NULL, watchdog_thread, NULL))
@@ -10950,7 +10949,7 @@ begin_bench:
 
 #ifdef HAVE_OPENCL
 	/* Create reinit gpu thread */
-	gpur_thr_id = 4;
+	gpur_thr_id = 3;
 	thr = &control_thr[gpur_thr_id];
 	thr->q = tq_new();
 	if (!thr->q)
@@ -10960,7 +10959,7 @@ begin_bench:
 #endif	
 
 	/* Create API socket thread */
-	api_thr_id = 5;
+	api_thr_id = 4;
 	thr = &control_thr[api_thr_id];
 	if (thr_info_create(thr, NULL, api_thread, thr))
 		quit(1, "API thread create failed");
@@ -10979,7 +10978,7 @@ begin_bench:
 	/* Create curses input thread for keyboard input. Create this last so
 	 * that we know all threads are created since this can call kill_work
 	 * to try and shut down ll previous threads. */
-	input_thr_id = 6;
+	input_thr_id = 5;
 	thr = &control_thr[input_thr_id];
 	if (thr_info_create(thr, NULL, input_thread, thr))
 		quit(1, "input thread create failed");
@@ -10987,7 +10986,7 @@ begin_bench:
 #endif
 
 	/* 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);
 
 	/* 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
 
-#ifdef NEED_ROUNDL
-#define roundl(x)   (long double)((long long)((x==0)?0.0:((x)+(((x)>0)?0.5:-0.5))))
-#endif
-
 enum alive {
 	LIFE_WELL,
 	LIFE_SICK,
@@ -584,6 +580,7 @@ struct cgpu_info {
 
 	pthread_rwlock_t qlock;
 	struct work *queued_work;
+	struct work *unqueued_work;
 	unsigned int queued_count;
 
 	bool disable_watchdog;
@@ -822,6 +819,13 @@ static inline void mutex_init(pthread_mutex_t *lock)
 		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)
 {
 	if (unlikely(pthread_rwlock_init(lock, NULL)))
@@ -836,12 +840,23 @@ struct cglock {
 
 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)
 {
 	mutex_init(&lock->mutex);
 	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. */
 static inline void cg_rlock(cglock_t *lock)
 {
@@ -945,6 +960,9 @@ extern bool opt_worktime;
 #ifdef USE_AVALON
 extern char *opt_avalon_options;
 #endif
+#ifdef USE_KLONDIKE
+extern char *opt_klondike_options;
+#endif
 #ifdef USE_BITFORCE
 extern bool opt_bfl_noncerange;
 #endif
@@ -1209,6 +1227,7 @@ struct pool {
 	int quota;
 	int quota_gcd;
 	int quota_used;
+	int works;
 
 	double diff_accepted;
 	double diff_rejected;
@@ -1335,7 +1354,6 @@ struct work {
 	bool		stale;
 	bool		mandatory;
 	bool		block;
-	bool		queued;
 
 	bool		stratum;
 	char 		*job_id;
@@ -1353,6 +1371,9 @@ struct work {
 	// Allow devices to identify work if multiple sub-devices
 	// DEPRECATED: New code should be using multiple processors instead
 	unsigned char	subid;
+	
+	// Allow devices to timestamp work for their own purposes
+	struct timeval	tv_stamp;
 
 	blktemplate_t	*tmpl;
 	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_nonce2(work, nonce)  (_test_nonce2(work, nonce, true))
 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 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_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 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);
@@ -1441,6 +1466,8 @@ enum api_data_type {
 	API_ESCAPE,
 	API_STRING,
 	API_CONST,
+	API_UINT8,
+	API_UINT16,
 	API_INT,
 	API_UINT,
 	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_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_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_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);

+ 0 - 10
sha2.c

@@ -39,16 +39,6 @@
 
 #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)                      \
 {                                             \
     *((str) + 3) = (uint8_t) ((x)      );       \

+ 12 - 0
sha2.h

@@ -44,6 +44,16 @@
 #define SHA256_DIGEST_SIZE ( 256 / 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 {
     unsigned int tot_len;
     unsigned int len;
@@ -51,6 +61,8 @@ typedef struct {
     uint32_t h[8];
 } sha256_ctx;
 
+extern uint32_t sha256_k[64];
+
 void sha256_init(sha256_ctx * ctx);
 void sha256_update(sha256_ctx *ctx, const unsigned char *message,
                    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);
 }
 
+#define ms_tdiff(end, start)  (timer_elapsed_us(start, end) / 1000)
+
 static inline
 int timer_elapsed(const struct timeval *tvp_timer, const struct timeval *tvp_now)
 {