Your Name 1 year ago
commit
c6b095c92c
100 changed files with 42784 additions and 0 deletions
  1. 56 0
      01-cgminer.rules
  2. 117 0
      A1-board-selector-CCD.c
  3. 186 0
      A1-board-selector-CCR.c
  4. 50 0
      A1-board-selector.h
  5. 91 0
      A1-common.h
  6. 158 0
      A1-desk-board-selector.c
  7. 116 0
      A1-trimpot-mcp4x.c
  8. 19 0
      A1-trimpot-mcp4x.h
  9. 1864 0
      API-README
  10. BIN
      API.class
  11. 166 0
      API.java
  12. 860 0
      ASIC-README
  13. 8 0
      AUTHORS
  14. 674 0
      COPYING
  15. 6 0
      ChangeLog
  16. 224 0
      FPGA-README
  17. 3 0
      LICENSE
  18. BIN
      MCast.class
  19. 333 0
      MCast.java
  20. 233 0
      Makefile.am
  21. 7412 0
      NEWS
  22. 19 0
      README.gekko.txt
  23. 90 0
      README.md
  24. 334 0
      api-example.c
  25. 118 0
      api-example.php
  26. 52 0
      api-example.py
  27. 38 0
      api-example.rb
  28. 5245 0
      api.c
  29. 26 0
      arg-nonnull.h
  30. 9 0
      autogen.sh
  31. 170 0
      bench_block.h
  32. 1230 0
      bf16-bitfury16.c
  33. 383 0
      bf16-bitfury16.h
  34. 122 0
      bf16-brd-control.c
  35. 43 0
      bf16-brd-control.h
  36. 317 0
      bf16-communication.c
  37. 23 0
      bf16-communication.h
  38. 182 0
      bf16-ctrldevice.c
  39. 43 0
      bf16-ctrldevice.h
  40. 21 0
      bf16-device.h
  41. 111 0
      bf16-gpiodevice.c
  42. 66 0
      bf16-gpiodevice.h
  43. 502 0
      bf16-mspcontrol.c
  44. 19 0
      bf16-mspcontrol.h
  45. 110 0
      bf16-spidevice.c
  46. 22 0
      bf16-spidevice.h
  47. 149 0
      bf16-uartdevice.c
  48. 23 0
      bf16-uartdevice.h
  49. 108 0
      bitforce-firmware-flash.c
  50. 2310 0
      bitmain-board-test.c
  51. 244 0
      bitmain-board-test.h
  52. 23 0
      bitstreams/COPYING_fpgaminer
  53. 1 0
      bitstreams/README
  54. BIN
      bitstreams/fpgaminer_top_fixed7_197MHz.ncd
  55. 271 0
      c++defs.h
  56. 4 0
      ccan/Makefile.am
  57. 165 0
      ccan/compiler/LICENSE
  58. 64 0
      ccan/compiler/_info
  59. 216 0
      ccan/compiler/compiler.h
  60. 22 0
      ccan/compiler/test/compile_fail-printf.c
  61. 15 0
      ccan/compiler/test/run-is_compile_constant.c
  62. 339 0
      ccan/opt/LICENSE
  63. 67 0
      ccan/opt/_info
  64. 199 0
      ccan/opt/helpers.c
  65. 255 0
      ccan/opt/opt.c
  66. 367 0
      ccan/opt/opt.h
  67. 132 0
      ccan/opt/parse.c
  68. 19 0
      ccan/opt/private.h
  69. 13 0
      ccan/opt/test/compile_ok-const-arg.c
  70. 144 0
      ccan/opt/test/run-checkopt.c
  71. 49 0
      ccan/opt/test/run-correct-reporting.c
  72. 440 0
      ccan/opt/test/run-helpers.c
  73. 88 0
      ccan/opt/test/run-iter.c
  74. 33 0
      ccan/opt/test/run-no-options.c
  75. 112 0
      ccan/opt/test/run-usage.c
  76. 297 0
      ccan/opt/test/run.c
  77. 110 0
      ccan/opt/test/utils.c
  78. 19 0
      ccan/opt/test/utils.h
  79. 111 0
      ccan/opt/usage.c
  80. 510 0
      ccan/typesafe_cb/LICENSE
  81. 151 0
      ccan/typesafe_cb/_info
  82. 23 0
      ccan/typesafe_cb/test/compile_fail-cast_if_type-promotable.c
  83. 27 0
      ccan/typesafe_cb/test/compile_fail-typesafe_cb-int.c
  84. 34 0
      ccan/typesafe_cb/test/compile_fail-typesafe_cb.c
  85. 43 0
      ccan/typesafe_cb/test/compile_fail-typesafe_cb_cast-multi.c
  86. 25 0
      ccan/typesafe_cb/test/compile_fail-typesafe_cb_cast.c
  87. 27 0
      ccan/typesafe_cb/test/compile_fail-typesafe_cb_postargs.c
  88. 28 0
      ccan/typesafe_cb/test/compile_fail-typesafe_cb_preargs.c
  89. 17 0
      ccan/typesafe_cb/test/compile_ok-typesafe_cb-NULL.c
  90. 49 0
      ccan/typesafe_cb/test/compile_ok-typesafe_cb-undefined.c
  91. 52 0
      ccan/typesafe_cb/test/compile_ok-typesafe_cb-vars.c
  92. 41 0
      ccan/typesafe_cb/test/compile_ok-typesafe_cb_cast.c
  93. 109 0
      ccan/typesafe_cb/test/run.c
  94. 133 0
      ccan/typesafe_cb/typesafe_cb.h
  95. 11652 0
      cgminer.c
  96. 89 0
      compat.h
  97. 3 0
      compat/.gitignore
  98. 11 0
      compat/Makefile.am
  99. 796 0
      compat/jansson-2.9/CHANGES
  100. 684 0
      compat/jansson-2.9/CMakeLists.txt

+ 56 - 0
01-cgminer.rules

@@ -0,0 +1,56 @@
+# Butterfly Labs FPGA and ASIC devices
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# ModMinerQuad
+ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0003", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# Lancelot and Avalon
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# Icarus
+ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0083", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# AsicminerUSB and Antminer U1
+ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# Cairnsmore1
+ATTRS{idVendor}=="067b", ATTRS{idProduct}=="0230", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# Cairnsmore1-2
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8350", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# Ztex
+ATTRS{idVendor}=="221a", ATTRS{idProduct}=="0100", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# BF1
+ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="204b", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# Klondike
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="f60a", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# HashFast
+ATTRS{idVendor}=="297c", ATTRS{idProduct}=="0001", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+ATTRS{idVendor}=="297c", ATTRS{idProduct}=="8001", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# BXF
+ATTRS{idVendor}=="198c", ATTRS{idProduct}=="b1f1", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# NF1
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="00de", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# ANT_S1
+ATTRS{idVendor}=="4254", ATTRS{idProduct}=="4153", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# Cointerra
+ATTRS{idVendor}=="1cbe", ATTRS{idProduct}=="0003", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# Drillbit Thumb
+ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2404", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# Avalon4
+ATTRS{idVendor}=="29f1", ATTRS{idProduct}=="33f2", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+
+# GekkoScience
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", SUBSYSTEM=="usb", ACTION=="add", MODE="0660", GROUP="plugdev"
+

+ 117 - 0
A1-board-selector-CCD.c

@@ -0,0 +1,117 @@
+/*
+ * board selector support for TCA9535 used in Bitmine's CoinCraft Desk
+ *
+ * Copyright 2014 Zefir Kurtisi <zefir.kurtisi@gmail.com>
+ *
+ * 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 <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <fcntl.h>
+
+#include "miner.h"
+
+#include "A1-board-selector.h"
+#include "i2c-context.h"
+
+static struct board_selector ccd_selector;
+
+struct i2c_ctx *U1_tca9535;
+uint8_t chain_mask = 0xff;
+uint8_t active_chain = 255;
+pthread_mutex_t lock;
+
+
+#define UNUSED_BITS 0xe0
+
+static void ccd_unlock(void)
+{
+	mutex_unlock(&lock);
+}
+
+static void ccd_exit(void)
+{
+	if (U1_tca9535 != NULL)
+		U1_tca9535->exit(U1_tca9535);
+}
+uint8_t retval = 0;
+
+extern struct board_selector *ccd_board_selector_init(void)
+{
+	mutex_init(&lock);
+	U1_tca9535 = i2c_slave_open(I2C_BUS, 0x27);
+	if (U1_tca9535 == NULL)
+		return NULL;
+	bool retval =	U1_tca9535->write(U1_tca9535, 0x06, 0xe0) &&
+			U1_tca9535->write(U1_tca9535, 0x07, 0xe0) &&
+			U1_tca9535->write(U1_tca9535, 0x02, 0x1f) &&
+			U1_tca9535->write(U1_tca9535, 0x03, 0x00);
+	if (retval)
+		return &ccd_selector;
+	ccd_exit();
+	return NULL;
+}
+
+static bool ccd_select(uint8_t chain)
+{
+	if (chain >= CCD_MAX_CHAINS)
+		return false;
+
+	mutex_lock(&lock);
+	if (active_chain == chain)
+		return true;
+
+	active_chain = chain;
+	chain_mask = 1 << active_chain;
+	return U1_tca9535->write(U1_tca9535, 0x02, ~chain_mask);
+}
+
+static bool __ccd_board_selector_reset(uint8_t mask)
+{
+	if (!U1_tca9535->write(U1_tca9535, 0x03, mask))
+		return false;
+	cgsleep_ms(RESET_LOW_TIME_MS);
+	if (!U1_tca9535->write(U1_tca9535, 0x03, 0x00))
+		return false;
+	cgsleep_ms(RESET_HI_TIME_MS);
+	return true;
+}
+// we assume we are already holding the mutex
+static bool ccd_reset(void)
+{
+	return __ccd_board_selector_reset(chain_mask);
+}
+
+static bool ccd_reset_all(void)
+{
+	mutex_lock(&lock);
+	bool retval = __ccd_board_selector_reset(0xff & ~UNUSED_BITS);
+	mutex_unlock(&lock);
+	return retval;
+}
+
+
+static struct board_selector ccd_selector = {
+	.select = ccd_select,
+	.release = ccd_unlock,
+	.exit = ccd_exit,
+	.reset = ccd_reset,
+	.reset_all = ccd_reset_all,
+	/* don't have a temp sensor dedicated to chain */
+	.get_temp = dummy_get_temp,
+};
+

+ 186 - 0
A1-board-selector-CCR.c

@@ -0,0 +1,186 @@
+/*
+ * board selector support for TCA9535 used in Bitmine's CoinCraft Desk
+ *
+ * Copyright 2014 Zefir Kurtisi <zefir.kurtisi@gmail.com>
+ *
+ * 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 "miner.h"
+
+#include "A1-board-selector.h"
+#include "i2c-context.h"
+
+
+static struct board_selector ccr_selector;
+
+static struct i2c_ctx *U1_tca9548;
+static struct i2c_ctx *U3_tca9535;
+static struct i2c_ctx *U4_tca9535;
+static uint8_t active_chain;
+static pthread_mutex_t lock;
+
+struct chain_mapping {
+	uint8_t chain_id;
+	uint8_t U1;
+	uint8_t U3p0;
+	uint8_t U3p1;
+};
+
+static const struct chain_mapping chain_mapping[CCR_MAX_CHAINS] = {
+	{  0, 0x01, 0x01, 0x00, },
+	{  1, 0x01, 0x00, 0x80, },
+	{  2, 0x02, 0x02, 0x00, },
+	{  3, 0x02, 0x00, 0x40, },
+	{  4, 0x04, 0x04, 0x00, },
+	{  5, 0x04, 0x00, 0x20, },
+	{  6, 0x08, 0x08, 0x00, },
+	{  7, 0x08, 0x00, 0x10, },
+	{  8, 0x10, 0x10, 0x00, },
+	{  9, 0x10, 0x00, 0x08, },
+	{ 10, 0x20, 0x20, 0x00, },
+	{ 11, 0x20, 0x00, 0x04, },
+	{ 12, 0x40, 0x40, 0x00, },
+	{ 13, 0x40, 0x00, 0x02, },
+	{ 14, 0x80, 0x80, 0x00, },
+	{ 15, 0x80, 0x00, 0x01, },
+};
+
+static void ccr_unlock(void)
+{
+	mutex_unlock(&lock);
+}
+
+static void ccr_exit(void)
+{
+	if (U1_tca9548 != NULL)
+		U1_tca9548->exit(U1_tca9548);
+	if (U3_tca9535 != NULL)
+		U3_tca9535->exit(U3_tca9535);
+	if (U4_tca9535 != NULL)
+		U4_tca9535->exit(U4_tca9535);
+}
+
+
+extern struct board_selector *ccr_board_selector_init(void)
+{
+	mutex_init(&lock);
+	applog(LOG_INFO, "ccr_board_selector_init()");
+
+	/* detect all i2c slaves */
+	U1_tca9548 = i2c_slave_open(I2C_BUS, 0x70);
+	U3_tca9535 = i2c_slave_open(I2C_BUS, 0x23);
+	U4_tca9535 = i2c_slave_open(I2C_BUS, 0x22);
+	if (U1_tca9548 == NULL || U3_tca9535 == NULL || U4_tca9535 == NULL)
+		goto fail;
+
+			/* init I2C multiplexer */
+	bool res =	U1_tca9548->write(U1_tca9548, 0x00, 0x00) &&
+			/* init reset selector */
+			U3_tca9535->write(U3_tca9535, 0x06, 0x00) &&
+			U3_tca9535->write(U3_tca9535, 0x07, 0x00) &&
+			U3_tca9535->write(U3_tca9535, 0x02, 0x00) &&
+			U3_tca9535->write(U3_tca9535, 0x03, 0x00) &&
+			/* init chain selector */
+			U4_tca9535->write(U4_tca9535, 0x06, 0x00) &&
+			U4_tca9535->write(U4_tca9535, 0x07, 0x00) &&
+			U4_tca9535->write(U4_tca9535, 0x02, 0x00) &&
+			U4_tca9535->write(U4_tca9535, 0x03, 0x00);
+
+	if (!res)
+		goto fail;
+
+	return &ccr_selector;
+
+fail:
+	ccr_exit();
+	return NULL;
+}
+
+static bool ccr_select(uint8_t chain)
+{
+	if (chain >= CCR_MAX_CHAINS)
+		return false;
+
+	mutex_lock(&lock);
+	if (active_chain == chain)
+		return true;
+
+	active_chain = chain;
+	const struct chain_mapping *cm = &chain_mapping[chain];
+
+	if (!U1_tca9548->write(U1_tca9548, cm->U1, cm->U1))
+		return false;
+
+	if (!U4_tca9535->write(U4_tca9535, 0x02, cm->U3p0) ||
+	    !U4_tca9535->write(U4_tca9535, 0x03, cm->U3p1))
+		return false;
+
+	/* sanity check: ensure i2c command has been written before we leave */
+	uint8_t tmp;
+	if (!U4_tca9535->read(U4_tca9535, 0x02, &tmp) || tmp != cm->U3p0) {
+		applog(LOG_ERR, "ccr_select: wrote 0x%02x, read 0x%02x",
+		       cm->U3p0, tmp);
+	}
+	applog(LOG_DEBUG, "selected chain %d", chain);
+	return true;
+}
+
+static bool __ccr_board_selector_reset(uint8_t p0, uint8_t p1)
+{
+	if (!U3_tca9535->write(U3_tca9535, 0x02, p0) ||
+	    !U3_tca9535->write(U3_tca9535, 0x03, p1))
+		return false;
+	cgsleep_ms(RESET_LOW_TIME_MS);
+	if (!U3_tca9535->write(U3_tca9535, 0x02, 0x00) ||
+	    !U3_tca9535->write(U3_tca9535, 0x03, 0x00))
+		return false;
+	cgsleep_ms(RESET_HI_TIME_MS);
+	return true;
+}
+// we assume we are already holding the mutex
+static bool ccr_reset(void)
+{
+	const struct chain_mapping *cm = &chain_mapping[active_chain];
+	applog(LOG_DEBUG, "resetting chain %d", cm->chain_id);
+	bool retval = __ccr_board_selector_reset(cm->U3p0, cm->U3p1);
+	return retval;
+}
+
+static bool ccr_reset_all(void)
+{
+	mutex_lock(&lock);
+	bool retval = __ccr_board_selector_reset(0xff, 0xff);
+	mutex_unlock(&lock);
+	return retval;
+}
+
+static uint8_t ccr_get_temp(uint8_t sensor_id)
+{
+	if ((active_chain & 1) != 0 || sensor_id != 0)
+		return 0;
+
+	struct i2c_ctx *U7 = i2c_slave_open(I2C_BUS, 0x4c);
+	if (U7 == NULL)
+		return 0;
+
+	uint8_t retval = 0;
+	if (!U7->read(U7, 0, &retval))
+		retval = 0;
+	U7->exit(U7);
+	return retval;
+}
+
+static struct board_selector ccr_selector = {
+	.select = ccr_select,
+	.release = ccr_unlock,
+	.exit = ccr_exit,
+	.reset = ccr_reset,
+	.reset_all = ccr_reset_all,
+	.get_temp = ccr_get_temp,
+};
+

+ 50 - 0
A1-board-selector.h

@@ -0,0 +1,50 @@
+#ifndef A1_BOARD_SELECTOR_H
+#define A1_BOARD_SELECTOR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define RESET_LOW_TIME_MS 200
+#define RESET_HI_TIME_MS  100
+
+struct board_selector {
+	/* destructor */
+	void (*exit)(void);
+	/* select board and chip chain for given chain index*/
+	bool (*select)(uint8_t chain);
+	/* release access to selected chain */
+	void (*release)(void);
+	/* reset currently selected chain */
+	bool (*reset)(void);
+	/* reset all chains on board */
+	bool (*reset_all)(void);
+	/* get temperature for selected chain at given sensor */
+	uint8_t (*get_temp)(uint8_t sensor);
+	/* prepare board (voltage) for given sys_clock */
+	bool (*prepare_clock)(int clock_khz);
+};
+
+static bool dummy_select(uint8_t b) { (void)b; return true; }
+static void dummy_void(void) { };
+static bool dummy_bool(void) { return true; }
+//static uint8_t dummy_u8(void) { return 0; }
+static uint8_t dummy_get_temp(uint8_t s) { (void)s; return 0; }
+static bool dummy_prepare_clock(int c) { (void)c; return true; }
+
+static const struct board_selector dummy_board_selector = {
+	.exit = dummy_void,
+	.select = dummy_select,
+	.release = dummy_void,
+	.reset = dummy_bool,
+	.reset_all = dummy_bool,
+	.get_temp = dummy_get_temp,
+	.prepare_clock = dummy_prepare_clock,
+};
+
+/* CoinCraft Desk and Rig board selector constructors */
+#define CCD_MAX_CHAINS	5
+#define CCR_MAX_CHAINS	16
+extern struct board_selector *ccd_board_selector_init(void);
+extern struct board_selector *ccr_board_selector_init(void);
+
+#endif /* A1_BOARD_SELECTOR_H */

+ 91 - 0
A1-common.h

@@ -0,0 +1,91 @@
+#ifndef A1_COMMON_H
+#define A1_COMMON_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/********** work queue */
+struct work_ent {
+	struct work *work;
+	struct list_head head;
+};
+
+struct work_queue {
+	int num_elems;
+	struct list_head head;
+};
+
+/********** chip and chain context structures */
+/* the WRITE_JOB command is the largest (2 bytes command, 56 bytes payload) */
+#define WRITE_JOB_LENGTH	58
+#define MAX_CHAIN_LENGTH	64
+/*
+ * For commands to traverse the chain, we need to issue dummy writes to
+ * keep SPI clock running. To reach the last chip in the chain, we need to
+ * write the command, followed by chain-length words to pass it through the
+ * chain and another chain-length words to get the ACK back to host
+ */
+#define MAX_CMD_LENGTH		(WRITE_JOB_LENGTH + MAX_CHAIN_LENGTH * 2 * 2)
+
+struct A1_chip {
+	int num_cores;
+	int last_queued_id;
+	struct work *work[4];
+	/* stats */
+	int hw_errors;
+	int stales;
+	int nonces_found;
+	int nonce_ranges_done;
+
+	/* systime in ms when chip was disabled */
+	int cooldown_begin;
+	/* number of consecutive failures to access the chip */
+	int fail_count;
+	/* mark chip disabled, do not try to re-enable it */
+	bool disabled;
+};
+
+struct A1_chain {
+	int chain_id;
+	struct cgpu_info *cgpu;
+	struct mcp4x *trimpot;
+	int num_chips;
+	int num_cores;
+	int num_active_chips;
+	int chain_skew;
+	uint8_t spi_tx[MAX_CMD_LENGTH];
+	uint8_t spi_rx[MAX_CMD_LENGTH];
+	struct spi_ctx *spi_ctx;
+	struct A1_chip *chips;
+	pthread_mutex_t lock;
+
+	struct work_queue active_wq;
+
+	/* mark chain disabled, do not try to re-enable it */
+	bool disabled;
+	uint8_t temp;
+	int last_temp_time;
+};
+
+#define MAX_CHAINS_PER_BOARD	2
+struct A1_board {
+	int board_id;
+	int num_chains;
+	struct A1_chain *chain[MAX_CHAINS_PER_BOARD];
+};
+
+/********** config paramters */
+struct A1_config_options {
+	int ref_clk_khz;
+	int sys_clk_khz;
+	int spi_clk_khz;
+	/* limit chip chain to this number of chips (testing only) */
+	int override_chip_num;
+	int wiper;
+};
+
+/* global configuration instance */
+extern struct A1_config_options A1_config_options;
+
+#endif /* A1_COMMON_H */

+ 158 - 0
A1-desk-board-selector.c

@@ -0,0 +1,158 @@
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <fcntl.h>
+
+#include "miner.h"
+
+struct pcf8575_ctx {
+	uint8_t addr;
+	uint8_t p0;
+	uint8_t p1;
+	int file;
+	uint8_t active_board;
+	pthread_mutex_t lock;
+};
+
+static struct pcf8575_ctx board_ctx = { 0x27, 0xff, 0xff, -1, .active_board = 255,};
+
+
+#define UNUSED_BITS 0xe0
+#define SLEEP_MS_AFTER_CS 0
+static bool pcf8575_write(void)
+{
+	union i2c_smbus_data data;
+	data.byte = board_ctx.p1 | UNUSED_BITS;
+
+	struct i2c_smbus_ioctl_data args;
+	__s32 err;
+
+	args.read_write = I2C_SMBUS_WRITE;
+	args.command = board_ctx.p0 | UNUSED_BITS;
+	args.size = I2C_SMBUS_BYTE_DATA;
+	args.data = &data;
+
+	err = ioctl(board_ctx.file, I2C_SMBUS, &args);
+	if (err == -1) {
+		fprintf(stderr,
+			"Error: Failed to write: %s\n",
+			strerror(errno));
+		err = -errno;
+	} else {
+		applog(LOG_DEBUG, "written: 0x%02x, 0x%02x", board_ctx.p0, board_ctx.p1);
+//		usleep(25000);
+		cgsleep_ms(SLEEP_MS_AFTER_CS);
+	}
+	return err == 0;
+}
+
+void lock_board_selector(void)
+{
+//	applog(LOG_WARNING, "lock_board_selector()");
+	mutex_lock(&board_ctx.lock);
+}
+
+void unlock_board_selector(void)
+{
+//	applog(LOG_WARNING, "unlock_board_selector()");
+	mutex_unlock(&board_ctx.lock);
+}
+
+bool a1_board_selector_init(void)
+{
+	mutex_init(&board_ctx.lock);
+	applog(LOG_WARNING, "a1_board_selector_init()");
+
+	board_ctx.file = open("/dev/i2c-1", O_RDWR);
+	if (board_ctx.file < 0) {
+		fprintf(stderr,
+			"Error: Could not open i2c-1: %s\n",
+			board_ctx.addr, strerror(errno));
+		return false;
+	}
+
+	if (ioctl(board_ctx.file, I2C_SLAVE, board_ctx.addr) < 0) {
+		fprintf(stderr,
+			"Error: Could not set address to 0x%02x: %s\n",
+			board_ctx.addr, strerror(errno));
+		return false;
+	}
+	return pcf8575_write();
+}
+
+void a1_board_selector_exit(void)
+{
+	close(board_ctx.file);
+	board_ctx.file = -1;
+}
+
+bool a1_board_selector_select_board(uint8_t board)
+{
+	if (board > 7)
+		return false;
+
+//	applog(LOG_WARNING, "board_selector_select_board(%d)", board);
+	lock_board_selector();
+	if (board_ctx.active_board == board)
+		return true;
+
+	board_ctx.active_board = board;
+	board_ctx.p0 = 1 << board_ctx.active_board;
+	board_ctx.p1 = 0xff;
+	bool retval = pcf8575_write();
+	return retval;
+}
+
+static bool __board_selector_reset(void)
+{
+	board_ctx.p1 = ~board_ctx.p0;
+	if (!pcf8575_write())
+		return false;
+	usleep(1000000);
+	board_ctx.p1 = 0xff;
+	if (!pcf8575_write())
+		return false;
+	usleep(1000000);
+	return true;
+}
+// we assume we are already holding the mutex
+bool a1_board_selector_reset_board(void)
+{
+//	lock_board_selector();
+	bool retval = __board_selector_reset();
+//	unlock_board_selector();
+	return retval;
+}
+
+bool a1_board_selector_reset_all_boards(void)
+{
+	lock_board_selector();
+	board_ctx.p1 = 0;
+	bool retval = __board_selector_reset();
+	unlock_board_selector();
+	return retval;
+}
+
+#if 0
+int main(void)
+{
+	if (init_pcf8575(&board_ctx)) {
+		if (!pcf8575_write(&g_ctx)) {
+			fprintf(stderr,
+				"Error: Failed to write: %s\n",
+				strerror(errno));
+		}
+		a1_board_selector_exit(&g_ctx);
+	}
+	return 0;
+}
+#endif
+/////////////////////////////////////////////////////////////////////////////

+ 116 - 0
A1-trimpot-mcp4x.c

@@ -0,0 +1,116 @@
+/*
+ * support for MCP46x digital trimpot used in Bitmine's products
+ *
+ * Copyright 2014 Zefir Kurtisi <zefir.kurtisi@gmail.com>
+ *
+ * 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 <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <sys/ioctl.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <fcntl.h>
+
+#include "miner.h"
+
+#include "A1-trimpot-mcp4x.h"
+
+
+static bool mcp4x_check_status(int file)
+{
+	union i2c_smbus_data data;
+	struct i2c_smbus_ioctl_data args;
+
+	args.read_write = I2C_SMBUS_READ;
+	args.command = ((5 & 0x0f) << 4) | 0x0c;
+	args.size = I2C_SMBUS_WORD_DATA;
+	args.data = &data;
+
+	return ioctl(file, I2C_SMBUS, &args) >= 0;
+}
+
+static uint16_t mcp4x_get_wiper(struct mcp4x *me, uint8_t id)
+{
+	assert(id < 2);
+	union i2c_smbus_data data;
+	struct i2c_smbus_ioctl_data args;
+
+	args.read_write = I2C_SMBUS_READ;
+	args.command = ((id & 0x0f) << 4) | 0x0c;
+	args.size = I2C_SMBUS_WORD_DATA;
+	args.data = &data;
+
+	if (ioctl(me->file, I2C_SMBUS, &args) < 0) {
+		applog(LOG_ERR, "Failed to read id %d: %s\n", id,
+		       strerror(errno));
+		return 0xffff;
+	}
+	return htobe16(data.word & 0xffff);
+}
+
+static bool mcp4x_set_wiper(struct mcp4x *me, uint8_t id, uint16_t w)
+{
+	assert(id < 2);
+	union i2c_smbus_data data;
+	data.word = w;
+
+	struct i2c_smbus_ioctl_data args;
+
+	args.read_write = I2C_SMBUS_WRITE;
+	args.command = (id & 0x0f) << 4;
+	args.size = I2C_SMBUS_WORD_DATA;
+	args.data = &data;
+
+	if (ioctl(me->file, I2C_SMBUS, &args) < 0) {
+		applog(LOG_ERR, "Failed to read id %d: %s\n", id,
+		       strerror(errno));
+		return false;
+	}
+	return me->get_wiper(me, id) == w;
+}
+
+void mcp4x_exit(struct mcp4x *me)
+{
+	close(me->file);
+	free(me);
+}
+
+struct mcp4x *mcp4x_init(uint8_t addr)
+{
+	struct mcp4x *me;
+	int file = open("/dev/i2c-1", O_RDWR);
+	if (file < 0) {
+		applog(LOG_INFO, "Failed to open i2c-1: %s\n", strerror(errno));
+		return NULL;
+	}
+
+	if (ioctl(file, I2C_SLAVE, addr) < 0)
+		return NULL;
+
+	if (!mcp4x_check_status(file))
+		return NULL;
+
+	me = malloc(sizeof(*me));
+	assert(me != NULL);
+
+	me->addr = addr;
+	me->file = file;
+	me->exit = mcp4x_exit;
+	me->get_wiper = mcp4x_get_wiper;
+	me->set_wiper = mcp4x_set_wiper;
+	return me;
+}
+

+ 19 - 0
A1-trimpot-mcp4x.h

@@ -0,0 +1,19 @@
+#ifndef TRIMPOT_MPC4X_H
+#define TRIMPOT_MPC4X_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+
+struct mcp4x {
+	uint16_t (*get_wiper)(struct mcp4x *me, uint8_t id);
+	bool (*set_wiper)(struct mcp4x *me, uint8_t id, uint16_t w);
+	void (*exit)(struct mcp4x *me);
+	uint8_t addr;
+	int file;
+};
+
+/* constructor */
+extern struct mcp4x *mcp4x_init(uint8_t addr);
+
+#endif /* TRIMPOT_MPC4X_H */

+ 1864 - 0
API-README

@@ -0,0 +1,1864 @@
+
+This README contains details about the cgminer RPC API
+
+It also includes some detailed information at the end,
+about using miner.php
+
+
+If you start cgminer with the "--api-listen" option, it will listen on a
+simple TCP/IP socket for single string API requests from the same machine
+running cgminer and reply with a string and then close the socket each time
+If you add the "--api-network" option, it will accept API requests from any
+network attached computer.
+
+You can only access the comands that reply with data in this mode.
+By default, you cannot access any privileged command that affects the miner -
+you will receive an access denied status message see --api-allow below.
+
+You can specify IP addresses/prefixes that are only allowed to access the API
+with the "--api-allow" option e.g. --api-allow W:192.168.0.1,10.0.0/24
+will allow 192.168.0.1 or any address matching 10.0.0.*, but nothing else
+IP addresses are automatically padded with extra '.0's as needed
+Without a /prefix is the same as specifying /32
+0/0 means all IP addresses.
+The 'W:' on the front gives that address/subnet privileged access to commands
+that modify cgminer (thus all API commands)
+Without it those commands return an access denied status.
+See --api-groups below to define other groups like W:
+Privileged access is checked in the order the IP addresses were supplied to
+"--api-allow"
+The first match determines the privilege level.
+Using the "--api-allow" option overides the "--api-network" option if they
+are both specified
+With "--api-allow", 127.0.0.1 is not by default given access unless specified
+
+If you start cgminer also with the "--api-mcast" option, it will listen for
+a multicast message and reply to it with a message containing it's API port
+number, but only if the IP address of the sender is allowed API access
+
+More groups (like the privileged group W:) can be defined using the
+--api-groups command
+Valid groups are only the letters A-Z (except R & W are predefined) and are
+not case sensitive
+The R: group is the same as not privileged access
+The W: group is (as stated) privileged access (thus all API commands)
+To give an IP address/subnet access to a group you use the group letter
+in front of the IP address instead of W: e.g. P:192.168.0/32
+An IP address/subnet can only be a member of one group
+A sample API group would be:
+ --api-groups
+        P:switchpool:enablepool:addpool:disablepool:removepool:poolpriority:*
+This would create a group 'P' that can do all current pool commands and all
+non-priviliged commands - the '*' means all non-priviledged commands
+Without the '*' the group would only have access to the pool commands
+Defining multiple groups example:
+ --api-groups Q:quit:restart:*,S:save
+This would define 2 groups:
+ Q: that can 'quit' and 'restart' as well as all non-priviledged commands
+ S: that can only 'save' and no other commands
+
+The RPC API request can be either simple text or JSON.
+
+If the request is JSON (starts with '{'), it will reply with a JSON formatted
+response, otherwise it replies with text formatted as described further below.
+
+The JSON request format required is '{"command":"CMD","parameter":"PARAM"}'
+(though of course parameter is not required for all requests)
+where "CMD" is from the "Request" column below and "PARAM" would be e.g.
+the ASC/PGA number if required.
+
+An example request in both formats to disable Hotplug:
+  hotplug|0
+  {"command":"hotplug","parameter":"0"}
+
+The format of each reply (unless stated otherwise) is a STATUS section
+followed by an optional detail section
+
+From API version 1.7 onwards, reply strings in JSON and Text have the
+necessary escaping as required to avoid ambiguity - they didn't before 1.7
+For JSON the 2 characters '"' and '\' are escaped with a '\' before them
+For Text the 4 characters '|' ',' '=' and '\' are escaped the same way
+
+Only user entered information will contain characters that require being
+escaped, such as Pool URL, User and Password or the Config save filename,
+when they are returned in messages or as their values by the API
+
+For API version 1.4 and later:
+
+The STATUS section is:
+
+ STATUS=X,When=NNN,Code=N,Msg=string,Description=string|
+
+  STATUS=X Where X is one of:
+   W - Warning
+   I - Informational
+   S - Success
+   E - Error
+   F - Fatal (code bug)
+
+  When=NNN
+   Standard long time of request in seconds
+
+  Code=N
+   Each unique reply has a unique Code (See api.c - #define MSG_NNNNNN)
+
+  Msg=string
+   Message matching the Code value N
+
+  Description=string
+   This defaults to the cgminer version but is the value of --api-description
+   if it was specified at runtime.
+
+With API V3.1 you can also request multiple report replies in a single command
+request
+e.g. to request both summary and devs, the command would be summary+devs
+
+This is only available for report commands that don't need parameters,
+and is not available for commands that change anything
+Any parameters supplied will be ignored
+
+The extra formatting of the result is to have a section for each command
+e.g. CMD=summary|STATUS=....|CMD=devs|STATUS=...
+With JSON, each result is within a section of the command name
+e.g. {"summary":{"STATUS":[{"STATUS":"S"...}],"SUMMARY":[...],"id":1},
+      "devs":{"STATUS":[{"STATUS:"S"...}],"DEVS":[...],"id":1},"id":1}
+
+As before, if you supply bad JSON you'll just get a single 'E' STATUS section
+in the old format, since it doesn't switch to using the new format until it
+correctly processes the JSON and can match a '+' in the command
+
+If you request a command multiple times, e.g. devs+devs
+you'll just get it once
+If this results in only one command, it will still use the new layout
+with just the one command
+
+If you request a command that can't be used due to requiring parameters,
+a command that isn't a report, or an invalid command, you'll get an 'E' STATUS
+for that one but it will still attempt to process all other commands supplied
+
+Blank/missing commands are ignored e.g. +devs++
+will just show 'devs' using the new layout
+
+For API version 1.10 and later:
+
+The list of requests - a (*) means it requires privileged access - and replies:
+
+ Request       Reply Section  Details
+ -------       -------------  -------
+ version       VERSION        CGMiner=cgminer, version
+                              API=API| version
+
+ config        CONFIG         Some miner configuration information:
+                              ASC Count=N, <- the number of ASCs
+                              PGA Count=N, <- the number of PGAs
+                              Pool Count=N, <- the number of Pools
+                              Strategy=Name, <- the current pool strategy
+                              Log Interval=N, <- log interval (--log N)
+                              Device Code=ICA , <- spaced list of compiled
+                                                       device drivers
+                              OS=Linux/Apple/..., <- operating System
+
+ summary       SUMMARY        The status summary of the miner
+                              e.g. Elapsed=NNN,Found Blocks=N,Getworks=N,...|
+
+ pools         POOLS          The status of each pool e.g.
+                              Pool=0,URL=http://pool.com:6311,Status=Alive,...|
+
+ devs          DEVS           Each available PGA and ASC with their details
+                              e.g. ASC=0,Accepted=NN,MHS av=NNN,...,Intensity=D|
+                              Last Share Time=NNN, <- standand long time in sec
+                               (or 0 if none) of last accepted share
+                              Last Share Pool=N, <- pool number (or -1 if none)
+                              Last Valid Work=NNN, <- standand long time in sec
+                               of last work returned that wasn't an HW:
+                              Will not report PGAs if PGA mining is disabled
+                              Will not report ASCs if ASC mining is disabled
+
+ edevs[|old]   DEVS           The same as devs, except it ignores blacklisted
+                              devices and zombie devices
+                              If you specify the optional 'old' parameter, then
+                              the output will include zombie devices that became
+                              zombies less than 'old' seconds ago
+                              A value of zero for 'old', which is the default,
+                              means ignore all zombies
+                              It will return an empty list of devices if all
+                              devices are blacklisted or zombies
+
+ pga|N         PGA            The details of a single PGA number N in the same
+                              format and details as for DEVS
+                              This is only available if PGA mining is enabled
+                              Use 'pgacount' or 'config' first to see if there
+                              are any
+
+ pgacount      PGAS           Count=N| <- the number of PGAs
+                              Always returns 0 if PGA mining is disabled
+
+ switchpool|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of switching pool N to the
+                              highest priority (the pool is also enabled)
+                              The Msg includes the pool URL
+
+ enablepool|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of enabling pool N
+                              The Msg includes the pool URL
+
+ addpool|URL,USR,PASS (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of attempting to add pool N
+                              The Msg includes the pool number and URL
+                              Use '\\' to get a '\' and '\,' to include a comma
+                              inside URL, USR or PASS
+
+ poolpriority|N,... (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of changing pool priorities
+                              See usage below
+
+ poolquota|N,Q (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of changing pool quota to Q
+
+ disablepool|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of disabling pool N
+                              The Msg includes the pool URL
+
+ removepool|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of removing pool N
+                              The Msg includes the pool URL
+                              N.B. all details for the pool will be lost
+
+ save|filename (*)
+               none           There is no reply section just the STATUS section
+                              stating success or failure saving the cgminer
+                              config to filename
+                              The filename is optional and will use the cgminer
+                              default if not specified
+
+ quit (*)      none           Status is a single "BYE" reply before cgminer
+                              quits
+
+ notify        NOTIFY         The last status and history count of each devices
+                              problem
+                              This lists all devices including those not
+                              supported by the 'devs' command e.g.
+                              NOTIFY=0,Name=ASC,ID=0,Last Well=1332432290,...|
+
+ privileged (*)
+               none           There is no reply section just the STATUS section
+                              stating an error if you do not have privileged
+                              access to the API and success if you do have
+                              privilege
+                              The command doesn't change anything in cgminer
+
+ pgaenable|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of the enable request
+                              You cannot enable a PGA if it's status is not WELL
+                              This is only available if PGA mining is enabled
+
+ pgadisable|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of the disable request
+                              This is only available if PGA mining is enabled
+
+ pgaidentify|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of the identify request
+                              This is only available if PGA mining is enabled
+                              and currently only BFL singles and Cairnsmore1's
+                              with the appropriate firmware support this command
+                              On a BFL single it will flash the led on the front
+                              of the device for appoximately 4s
+                              All other non BFL,ICA PGA devices will return a
+                              warning status message stating that they dont
+                              support it. Non-CMR ICAs will ignore the command.
+                              This adds a 4s delay to the BFL share being
+                              processed so you may get a message stating that
+                              procssing took longer than 7000ms if the request
+                              was sent towards the end of the timing of any work
+                              being worked on
+                              e.g.: BFL0: took 8438ms - longer than 7000ms
+                              You should ignore this
+
+ devdetails    DEVDETAILS     Each device with a list of their static details
+                              This lists all devices including those not
+                              supported by the 'devs' command
+                              e.g. DEVDETAILS=0,Name=ASC,ID=0,Driver=yuu,...|
+
+ restart (*)   none           Status is a single "RESTART" reply before cgminer
+                              restarts
+
+ stats         STATS          Each device or pool that has 1 or more getworks
+                              with a list of stats regarding getwork times
+                              The values returned by stats may change in future
+                              versions thus would not normally be displayed
+                              Device drivers are also able to add stats to the
+                              end of the details returned
+
+ estats[|old]  STATS          The same as stats, except it ignores blacklisted
+                              devices, zombie devices and pools
+                              If you specify the optional 'old' parameter, then
+                              the output will include zombie devices that became
+                              zombies less than 'old' seconds ago
+                              A value of zero for 'old', which is the default,
+                              means ignore all zombies
+                              It will return an empty list of devices if all
+                              devices are blacklisted or zombies
+
+ check|cmd     CHECK          Exists=Y/N, <- 'cmd' exists in this version
+                              Access=Y/N| <- you have access to use 'cmd'
+
+ failover-only|true/false (*)
+               none           This command has been deprecated, only returning a
+                              deprecated message.
+
+ coin          COIN           Coin mining information:
+                              Hash Method=sha256/scrypt,
+                              Current Block Time=N.N, <- 0 means none
+                              Current Block Hash=XXXX..., <- blank if none
+                              LP=true/false, <- LP is in use on at least 1 pool
+                              Network Difficulty=NN.NN|
+
+ debug|setting (*)
+               DEBUG          Debug settings
+                              The optional commands for 'setting' are the same
+                              as the screen curses debug settings
+                              You can only specify one setting
+                              Only the first character is checked - case
+                              insensitive:
+                              Silent, Quiet, Verbose, Debug, RPCProto,
+                              PerDevice, WorkTime, Normal
+                              The output fields are (as above):
+                              Silent=true/false,
+                              Quiet=true/false,
+                              Verbose=true/false,
+                              Debug=true/false,
+                              RPCProto=true/false,
+                              PerDevice=true/false,
+                              WorkTime=true/false|
+
+ setconfig|name,N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of setting 'name' to N
+                              No values are supported any more and only a
+                              deprecated message will be returned.
+
+ usbstats      USBSTATS       Stats of all LIBUSB mining devices except ztex
+                              e.g. Name=MMQ,ID=0,Stat=SendWork,Count=99,...|
+
+ pgaset|N,opt[,val] (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of setting PGA N with
+                              opt[,val]
+                              This is only available if PGA mining is enabled
+
+                              If the PGA does not support any set options, it
+                              will always return a WARN stating pgaset isn't
+                              supported
+
+                              If opt=help it will return an INFO status with a
+                              help message about the options available
+
+                              The current options are:
+                               MMQ opt=clock val=160 to 230 (a multiple of 2)
+                               CMR opt=clock val=100 to 220
+
+ zero|Which,true/false (*)
+               none           There is no reply section just the STATUS section
+                              stating that the zero, and optional summary, was
+                              done
+                              If Which='all', all normal cgminer and API
+                              statistics will be zeroed other than the numbers
+                              displayed by the usbstats and stats commands
+                              If Which='bestshare', only the 'Best Share' values
+                              are zeroed for each pool and the global
+                              'Best Share'
+                              The true/false option determines if a full summary
+                              is shown on the cgminer display like is normally
+                              displayed on exit.
+
+ hotplug|N (*) none           There is no reply section just the STATUS section
+                              stating that the hotplug setting succeeded
+                              If the code is not compiled with hotplug in it,
+                              the the warning reply will be
+                               'Hotplug is not available'
+                              If N=0 then hotplug will be disabled
+                              If N>0 && <=9999, then hotplug will check for new
+                              devices every N seconds
+
+ asc|N         ASC            The details of a single ASC number N in the same
+                              format and details as for DEVS
+                              This is only available if ASC mining is enabled
+                              Use 'asccount' or 'config' first to see if there
+                              are any
+
+ ascenable|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of the enable request
+                              You cannot enable a ASC if it's status is not WELL
+                              This is only available if ASC mining is enabled
+
+ ascdisable|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of the disable request
+                              This is only available if ASC mining is enabled
+
+ ascidentify|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of the identify request
+                              This is only available if ASC mining is enabled
+                              and currently only BFL ASICs support this command
+                              On a BFL single it will flash the led on the front
+                              of the device for appoximately 4s
+                              All other non BFL ASIC devices will return a
+                              warning status message stating that they dont
+                              support it
+
+ asccount      ASCS           Count=N| <- the number of ASCs
+                              Always returns 0 if ASC mining is disabled
+
+ ascset|N,opt[,val] (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of setting ASC N with
+                              opt[,val]
+                              This is only available if ASC mining is enabled
+
+                              If the ASC does not support any set options, it
+                              will always return a WARN stating ascset isn't
+                              supported
+
+                              If opt=help it will return an INFO status with a
+                              help message about the options available
+
+                              The current options are:
+                               AVA+BTB opt=freq val=256 to 1024 - chip frequency
+                               BTB opt=millivolts val=1000 to 1400 - corevoltage
+                               MBA opt=reset val=0 to chipcount - reset a chip
+                               BMA opt=volt val=0-9 opt=clock val=0-15
+                               MBA opt=freq val=0-chip:100-1400 - set chip freq
+                               MBA opt=ledcount val=0-100 - chip count for led
+                               MBA opt=ledlimit val=0-200 - led off below GHs
+                               MBA opt=spidelay val=0-9999 - SPI per I/O delay
+                               MBA opt=spireset i|s0-9999 - SPI regular reset
+                               MBA opt=spisleep val=0-9999 - SPI reset sleep ms
+
+ lcd           LCD            An all-in-one short status summary of the miner
+                              e.g. Elapsed,GHS av,GHS 5m,GHS 5s,Temp,
+                                   Last Share Difficulty,Last Share Time,
+                                   Best Share,Last Valid Work,Found Blocks,
+                                   Pool,User|
+
+ lockstats (*) none           There is no reply section just the STATUS section
+                              stating the results of the request
+                              A warning reply means lock stats are not compiled
+                              into cgminer
+                              The API writes all the lock stats to stderr
+
+When you enable, disable or restart a PGA or ASC, you will also get
+Thread messages in the cgminer status window
+
+The 'poolpriority' command can be used to reset the priority order of multiple
+pools with a single command - 'switchpool' only sets a single pool to first
+priority
+Each pool should be listed by id number in order of preference (first = most
+preferred)
+Any pools not listed will be prioritised after the ones that are listed, in the
+priority order they were originally
+If the priority change affects the miner's preference for mining, it may switch
+immediately
+
+When you switch to a different pool to the current one (including by priority
+change), you will get a 'Switching to URL' message in the cgminer status
+windows
+
+Obviously, the JSON format is simply just the names as given before the '='
+with the values after the '='
+
+If you enable cgminer debug (-D or --debug) or, when cgminer debug is off,
+turn on debug with the API command 'debug|debug' you will also get messages
+showing some details of the requests received and the replies
+
+There are included 4 program examples for accessing the API:
+
+api-example.php - a php script to access the API
+  usAge: php api-example.php command
+ by default it sends a 'summary' request to the miner at 127.0.0.1:4028
+ If you specify a command it will send that request instead
+ You must modify the line "$socket = getsock('127.0.0.1', 4028);" at the
+ beginning of "function request($cmd)" to change where it looks for cgminer
+
+api-example.rb - a Ruby script to access the API.
+ usage: ruby api-example.rb command[:parameter] [HOST [PORT]]
+This script prints the parsed cgminer API response
+
+API.java/API.class
+ a java program to access the API (with source code)
+  usAge is: java API command address port
+ Any missing or blank parameters are replaced as if you entered:
+  java API summary 127.0.0.1 4028
+
+api-example.c - a 'C' program to access the API (with source code)
+  usAge: api-example [command [ip/host [port]]]
+ again, as above, missing or blank parameters are replaced as if you entered:
+  api-example summary 127.0.0.1 4028
+
+miner.php - an example web page to access the API
+ This includes buttons and inputs to attempt access to the privileged commands
+ See the end of this API-README for details of how to tune the display
+ and also to use the option to display a multi-rig summary
+
+----------
+
+Feature Changelog for external applications using the API:
+
+---------
+
+API V3.7 (cgminer v4.9.3?)
+
+Modified API commands:
+ 'pools' - add 'Work Difficulty'
+
+---------
+
+API V3.6 (cgminer v4.9.2)
+
+Modified API commands:
+ 'pools' - add 'Bad Work'
+ 'setconfig' - expire, scantime and queue have all been deprecated and will
+ only return a deprecated message.
+ 'failover-only' - deprecated, a deprecated message will be returned.
+
+---------
+
+API V3.5 (cgminer v4.7.0)
+
+- Made quit and restart return valid JSON as a STATUS mirroring the request.
+- Made addpool return what pool number the added pool is.
+
+---------
+
+API V3.4 (cgminer v4.3.?)
+
+Added API commands:
+ 'lcd' - An all-in-one short status summary of the miner
+
+---------
+
+API V3.3 (cgminer v4.2.0)
+
+Added API commands:
+ 'edevs' - Only enabled devices, for 'devs'
+ 'estats' - Only enabled devices, for 'stats'
+
+---------
+
+API V3.2 (cgminer v4.1.0)
+
+Fix for:
+HEX32 data type in the API version v3.1 JSON - since cgminer v3.12.1 -
+returns an incorrect formatted json data element for the API stats command
+for HashFast hardware
+
+---------
+
+API V3.1 (cgminer v3.12.1)
+
+Multiple report request command with '+' e.g. summary+devs
+
+---------
+
+API V3.0 (cgminer v3.11.0)
+
+Allow unlimited size replies
+
+---------
+
+API V2.0 (cgminer v3.8.0)
+
+Removed all GPU related commands and information from the replies
+
+---------
+
+API V1.32 (cgminer v3.6.5)
+
+Modified API commands:
+ 'devs' 'gpu' 'pga' and 'asc' - add 'Device Elapsed'
+
+---------
+
+API V1.31 (cgminer v3.6.3)
+
+Added API command:
+ 'lockstats' - display cgminer dev lock stats if compiled in
+
+Modified API command:
+ 'summary' - add 'MHS %ds' (where %d is the log interval)
+
+---------
+
+API V1.30 (cgminer v3.4.3)
+
+Added API command:
+ 'poolquota' - Set pool quota for load-balance strategy.
+
+Modified API command:
+ 'pools' - add 'Quota'
+
+---------
+
+API V1.29 (cgminer v3.4.1)
+
+Muticast identification added to the API
+
+----------
+
+API V1.28 (cgminer v3.3.4)
+
+Modified API commands:
+ 'devs', 'pga', 'asc', 'gpu' - add 'Device Hardware%' and 'Device Rejected%'
+ 'pools' - add 'Pool Rejected%' and 'Pool Stale%'
+ 'summary' - add 'Device Hardware%', 'Device Rejected%', 'Pool Rejected%',
+                 'Pool Stale%'
+
+----------
+
+API V1.27 (cgminer v3.3.2)
+
+Added API commands:
+ 'ascset' - with: BTB opt=millivolts val=1000 to 1310 - core voltage
+                  AVA+BTB opt=freq val=256 to 450 - chip frequency
+
+----------
+
+API V1.26 (cgminer v3.2.3)
+
+Remove all CPU support (cgminer v3.0.0)
+
+Added API commands:
+ 'asc'
+ 'ascenable'
+ 'ascdisable'
+ 'ascidentify|N' (only works for BFL ASICs so far)
+ 'asccount'
+
+Various additions to the debug 'stats' command
+
+----------
+
+API V1.25
+
+Added API commands:
+ 'hotplug'
+
+Modified API commands:
+ 'devs' 'gpu' and 'pga' - add 'Last Valid Work'
+ 'devs' - list ASIC devices
+ 'config' - add 'Hotplug', 'ASC Count'
+ 'coin' - add 'Network Difficulty'
+
+----------
+
+API V1.24 (cgminer v2.11.0)
+
+Added API commands:
+ 'zero'
+
+Modified API commands:
+ 'pools' - add 'Best Share'
+ 'devs' and 'pga' - add 'No Device' for PGAs if MMQ or BFL compiled
+ 'stats' - add pool: 'Net Bytes Sent', 'Net Bytes Recv'
+
+----------
+
+API V1.23 (cgminer v2.10.2)
+
+Added API commands:
+ 'pgaset' - with: MMQ opt=clock val=160 to 230 (and a multiple of 2)
+
+----------
+
+API V1.22 (cgminer v2.10.1)
+
+Enforced output limitation:
+ all extra records beyond the output limit of the API (~64k) are ignored
+  and chopped off at the record boundary before the limit is reached
+  however, JSON brackets will be correctly closed and the JSON id will be
+  set to 0 (instead of 1) if any data was truncated
+
+Modified API commands:
+ 'stats' - add 'Times Sent', 'Bytes Sent', 'Times Recv', 'Bytes Recv'
+
+----------
+
+API V1.21 (cgminer v2.10.0)
+
+Added API commands:
+ 'usbstats'
+
+Modified API commands:
+ 'summary' - add 'Best Share'
+
+Modified output:
+ each MMQ shows up as 4 devices, each with it's own stats
+
+----------
+
+API V1.20 (cgminer v2.8.5)
+
+Modified API commands:
+ 'pools' - add 'Has Stratum', 'Stratum Active', 'Stratum URL'
+
+----------
+
+API V1.19 (cgminer v2.7.6)
+
+Added API commands:
+ 'debug'
+ 'pgaidentify|N' (only works for BFL Singles so far)
+ 'setconfig|name,N'
+
+Modified API commands:
+ 'devs' - add 'Diff1 Work', 'Difficulty Accepted', 'Difficulty Rejected',
+              'Last Share Difficulty' to all devices
+ 'gpu|N' - add 'Diff1 Work', 'Difficulty Accepted',
+              'Difficulty Rejected', 'Last Share Difficulty'
+ 'pga|N' - add 'Diff1 Work', 'Difficulty Accepted',
+              'Difficulty Rejected', 'Last Share Difficulty'
+ 'notify' - add '*Dev Throttle' (for BFL Singles)
+ 'pools' - add 'Proxy Type', 'Proxy', 'Difficulty Accepted',
+               'Difficulty Rejected', 'Difficulty Stale',
+               'Last Share Difficulty'
+ 'config' - add 'Queue', 'Expiry'
+ 'stats' - add 'Work Diff', 'Min Diff', 'Max Diff', 'Min Diff Count',
+               'Max Diff Count' to the pool stats
+
+----------
+
+API V1.18 (cgminer v2.7.4)
+
+Modified API commands:
+ 'stats' - add 'Work Had Roll Time', 'Work Can Roll', 'Work Had Expire',
+		'Work Roll Time' to the pool stats
+ 'config' - include 'ScanTime'
+
+----------
+
+API V1.17 (cgminer v2.7.1)
+
+Added API commands:
+ 'coin'
+
+Modified API commands:
+ 'summary' - add 'Work Utility'
+ 'pools' - add 'Diff1 Shares'
+
+----------
+
+API V1.16 (cgminer v2.6.5)
+
+Added API commands:
+ 'failover-only'
+
+Modified API commands:
+ 'config' - include failover-only state
+
+----------
+
+API V1.15 (cgminer v2.6.1)
+
+Added API commands:
+ 'poolpriority'
+
+----------
+
+API V1.14 (cgminer v2.5.0)
+
+Modified API commands:
+ 'stats' - more icarus timing stats added
+ 'notify' - include new device comms error counter
+
+The internal code for handling data was rewritten (~25% of the code)
+Completely backward compatible
+
+----------
+
+API V1.13 (cgminer v2.4.4)
+
+Added API commands:
+ 'check'
+
+Support was added to cgminer for API access groups with the --api-groups option
+It's 100% backward compatible with previous --api-access commands
+
+----------
+
+API V1.12 (cgminer v2.4.3)
+
+Modified API commands:
+ 'stats' - more pool stats added
+
+Support for the ModMinerQuad FPGA was added
+
+----------
+
+API V1.11 (cgminer v2.4.2)
+
+Modified API commands:
+ 'save' no longer requires a filename (use default if not specified)
+
+'save' incorrectly returned status E (error) on success before.
+It now correctly returns S (success)
+
+----------
+
+API V1.10 (cgminer v2.4.1)
+
+Added API commands:
+ 'stats'
+
+N.B. the 'stats' command can change at any time so any specific content
+present should not be relied upon.
+The data content is mainly used for debugging purposes or hidden options
+in cgminer and can change as development work requires
+
+Modified API commands:
+ 'pools' added "Last Share Time"
+
+----------
+
+API V1.9 (cgminer v2.4.0)
+
+Added API commands:
+ 'restart'
+
+Modified API commands:
+ 'notify' corrected invalid JSON
+
+----------
+
+API V1.8 (cgminer v2.3.5)
+
+Added API commands:
+ 'devdetails'
+
+Support for the ZTex FPGA was added
+
+----------
+
+API V1.7 (cgminer v2.3.4)
+
+Added API commands:
+ 'removepool'
+
+Modified API commands:
+ 'pools' added "User"
+
+From API version 1.7 onwards, reply strings in JSON and Text have the
+necessary escaping as required to avoid ambiguity
+For JSON the 2 characters '"' and '\' are escaped with a '\' before them
+For Text the 4 characters '|' ',' '=' and '\' are escaped the same way
+
+----------
+
+API V1.6 (cgminer v2.3.2)
+
+Added API commands:
+ 'pga'
+ 'pgaenable'
+ 'pgadisable'
+ 'pgacount'
+
+Modified API commands:
+ 'devs' now includes Icarus and Bitforce FPGA devices
+ 'notify' added "*" to the front of the name of all numeric error fields
+ 'config' correct "Log Interval" to use numeric (not text) type for JSON
+
+Support for Icarus and Bitforce FPGAs was added
+
+----------
+
+API V1.5 was not released
+
+----------
+
+API V1.4 (Kano's interim release of cgminer v2.3.1)
+
+Added API commands:
+ 'notify'
+
+Modified API commands:
+ 'config' added "Device Code" and "OS"
+
+Added "When" to the STATUS reply section of all commands
+
+----------
+
+API V1.3 (cgminer v2.3.1-2)
+
+Added API commands:
+ 'addpool'
+
+Modified API commands:
+ 'devs'/'gpu' added "Total MH" for each device
+ 'summary' added "Total MH"
+
+----------
+
+API V1.2 (cgminer v2.3.0)
+
+Added API commands:
+ 'enablepool'
+ 'disablepool'
+ 'privileged'
+
+Modified API commands:
+ 'config' added "Log Interval"
+
+Starting with API V1.2, any attempt to access a command that requires
+privileged security, from an IP address that does not have privileged
+security, will return an "Access denied" Error Status
+
+----------
+
+API V1.1 (cgminer v2.2.4)
+
+There were no changes to the API commands in cgminer v2.2.4,
+however support was added to cgminer for IP address restrictions
+with the --api-allow option
+
+----------
+
+API V1.1 (cgminer v2.2.2)
+
+Prior to V1.1, devs/gpu incorrectly reported GPU0 Intensity for all GPUs
+
+Modified API commands:
+ 'devs'/'gpu' added "Last Share Pool" and "Last Share Time" for each device
+
+----------
+
+API V1.0 (cgminer v2.2.0)
+
+Remove default CPU support
+
+Added API commands:
+ 'config'
+ 'gpucount'
+ 'cpucount'
+ 'switchpool'
+ 'gpuintensity'
+ 'gpumem'
+ 'gpuengine'
+ 'gpufan'
+ 'gpuvddc'
+ 'save'
+
+----------
+
+API V0.7 (cgminer v2.1.0)
+
+Initial release of the API in the main cgminer git
+
+Commands:
+ 'version'
+ 'devs'
+ 'pools'
+ 'summary'
+ 'gpuenable'
+ 'gpudisable'
+ 'gpurestart'
+ 'gpu'
+ 'cpu'
+ 'gpucount'
+ 'cpucount'
+ 'quit'
+
+----------------------------------------
+
+miner.php
+=========
+
+miner.php is a PHP based interface to the cgminer RPC API
+(referred to simply as the API below)
+
+It can show rig details, summaries and input fields to allow you to change
+cgminer
+You can also create custom summary pages with it
+
+It has two levels to the security:
+1) cgminer can be configured to allow or disallow API access and access level
+   security for miner.php
+2) miner.php can be configured to allow or disallow privileged cgminer
+   access, if cgminer is configured to allow privileged access for miner.php
+
+---------
+
+To use miner.php requires a web server with PHP
+
+Basics: On ubuntu, to install apache2 and php, the commands are:
+ sudo apt-get install apache2
+ sudo apt-get install php
+ sudo service apache2 restart
+
+On Fedora 17:
+ yum install httpd php
+ systemctl restart httpd.service
+ systemctl enable httpd.service --system
+
+On windows there are a few options.
+Try one of these (apparently the first one is easiest - thanks jborkl)
+ http://www.easyphp.org/
+ http://www.apachefriends.org/en/xampp.html
+ http://www.wampserver.com/en/
+
+---------
+
+The basic cgminer option to enable the API is:
+
+ --api-listen
+
+or in your cgminer.conf
+
+ "api-listen" : true,
+
+(without the ',' on the end if it is the last item)
+
+If the web server is running on the cgminer computer, the above
+is the only change required to give miner.php basic access to
+the cgminer API
+
+-
+
+If the web server runs on a different computer to cgminer,
+you will also need to tell cgminer to allow the web server
+to access cgminer's API and tell miner.php where cgminer is
+
+Assuming a.b.c.d is the IP address of the web server, you
+would add the following to cgminer:
+
+ --api-listen --api-allow a.b.c.d
+
+or in your cgminer.conf
+
+ "api-listen" : true,
+ "api-allow" : "a.b.c.d",
+
+to tell cgminer to give the web server read access to the API
+
+You also need to tell miner.php where cgminer is.
+Assuming cgminer is at IP address e.f.g.h, then you would
+edit miner.php and change the line
+
+ $rigs = array('127.0.0.1:4028');
+
+to
+
+ $rigs = array('e.f.g.h:4028');
+
+See --api-network or --api-allow for more access details
+and how to give write access
+
+You can however, also tell miner.php to find your cgminer rigs automatically
+on the local subnet
+
+Add the following to each cgminer:
+
+ --api-mcast
+
+or in your cgminer.conf
+
+ "api-mcast" : true,
+
+And in miner.php set $mcast = true;
+
+This will ignore the value of $rigs and overwrite it with the list of zero or
+more rigs found on the network in the timeout specified
+A rig will not reply if the API settings would mean it would also ignore an
+API request from the web server running miner.php
+
+---------
+
+Once you have a web server with PHP running
+
+ copy your miner.php to the main web folder
+
+On Xubuntu 11.04
+ /var/www/
+
+On Fedora 17
+ /var/www/html/
+
+On Windows
+ see your windows Web/PHP documentation
+
+Assuming the IP address of the web server is a.b.c.d
+Then in your web browser go to:
+
+ http://a.b.c.d/miner.php
+
+Done :)
+
+---------
+
+The rest of this documentation deals with the more complex
+functions of miner.php, using myminer.php, creaing custom
+summaries and displaying multiple cgminer rigs
+
+---------
+
+If you create a file called myminer.php in the same web folder
+where you put miner.php, miner.php will load it when it runs
+
+This is useful, to put any changes you need to make to miner.php
+instead of changing miner.php
+Thus if you update/get a new miner.php, you won't lose the changes
+you have made if you put all your changes in myminer.php
+(and don't change miner.php at all)
+
+A simple example myminer.php that defines 2 rigs
+(that I will keep referring to further below) is:
+
+<?php
+#
+$rigs = array('192.168.0.100:4028:A', '192.168.0.102:4028:B');
+#
+?>
+
+Changes in myminer.php superscede what is in miner.php
+However, this is only valid for variables in miner.php before the
+2 lines where myminer.php is included by miner.php:
+
+ if (file_exists('myminer.php'))
+  include_once('myminer.php');
+ 
+Every variable in miner.php above those 2 lines, can be changed by
+simply defining them in your myminer.php
+
+So although miner.php originally contains the line
+
+ $rigs = array('127.0.0.1:4028');
+
+if you created the example myminer.php given above, it would actually
+change the value of $rigs that is used when miner.php is running
+i.e. you don't have to remove or comment out the $rigs line in miner.php
+It will be superceded by myminer.php
+
+---------
+
+The example myminer.php above also shows how to define more that one rig
+to be shown my miner.php
+
+Each rig string is 2 or 3 values seperated by colons ':'
+They are simply an IP address or host name, followed by the
+port number (usually 4028) and an optional Name string
+
+miner.php displays rig buttons that will show the defails of a single
+rig when you click on it - the button shows either the rig number,
+or the 'Name' string if you provide it
+
+PHP arrays contain each string seperated by a comma, but no comma after
+the last one
+
+So an example for 3 rigs would be:
+
+ $rigs = array('192.168.0.100:4028:A', '192.168.0.102:4028:B',
+               '192.168.0.110:4028:C');
+
+Of course each of the rigs listed would also have to have the API
+running and be set to allow the web server to access the API - as
+explained before
+
+---------
+
+So basically, any variable explained below can be put in myminer.php
+if you wanted to set it to something different to it's default value
+and did not want to change miner.php itself every time you updated it
+
+Below is each variable that can be changed and an explanation of each
+
+---------
+
+Default:
+ $dfmt = 'H:i:s j-M-Y \U\T\CP';
+
+Define the date format used to print full length dates
+If you get the string 'UTCP' on the end of your dates shown, that
+means you are using an older version of PHP and you can instead use:
+ $dfmt = 'H:i:s j-M-Y \U\T\CO';
+
+The PHP documentation on the date format is here:
+ http://us.php.net/manual/en/function.date.php
+
+---------
+
+Default:
+ $title = 'Mine';
+
+Web page title
+If you know PHP you can of course use code to define it e.g.
+ $title = 'My Rig at: '.date($dfmt);
+
+Which would set the web page title to something like:
+ My Rig at: 10:34:00 22-Aug-2012 UTC+10:00
+
+---------
+
+Default:
+ $readonly = false;
+
+Set $readonly to true to force miner.php to be readonly
+This means it won't allow you to change cgminer even if the cgminer API
+options allow it to
+
+If you set $readonly to false then it will check cgminer 'privileged'
+and will show input fields and buttons on the single rig page
+allowing you to change devices, pools and even quit or restart
+cgminer
+
+However, if the 'privileged' test fails, the code will set $readonly to
+true
+
+---------
+
+Default:
+ $userlist = null;
+
+Define password checking and default access
+ null means there is no password checking
+
+$userlist is an array of 3 arrays e.g.
+$userlist = array('sys' => array('boss' => 'bpass'),
+                  'usr' => array('user' => 'upass', 'pleb' => 'ppass'),
+                  'def' => array('Pools'));
+
+'sys' is an array of system users and passwords (full access)
+'usr' is an array of user level users and passwords (readonly access)
+'def' is an array of custompages that anyone not logged in can view
+
+Any of the 3 can be null, meaning there are none of that item
+
+All validated 'usr' users are given $readonly = true; access
+All validated 'sys' users are given the $readonly access you defined
+
+If 'def' has one or more values, and allowcustompages is true, then
+anyone without a password can see the list of custompage buttons given
+in 'def' and will see the first one when they go to the web page, with
+a login button at the top right
+
+From the login page, if you login with no username or password, it will
+show the first 'def' custompage (if there are any)
+
+If you are logged in, it will show a logout button at the top right
+
+---------
+
+Default:
+ $notify = true;
+
+Set $notify to false to NOT attempt to display the notify command
+table of data
+
+Set $notify to true to attempt to display the notify command on
+the single rig page
+If your older version of cgminer returns an 'Invalid command'
+coz it doesn't have notify - it just shows the error status table
+
+---------
+
+Default:
+ $checklastshare = true;
+
+Set $checklastshare to true to do the following checks:
+If a device's last share is 12x expected ago then display as an error
+If a device's last share is 8x expected ago then display as a warning
+If either of the above is true, also display the whole line highlighted
+This assumes shares are 1 difficulty shares
+
+Set $checklastshare to false to not do the above checks
+
+'expected' is calculated from the device MH/s value
+So for example, a device that hashes at 380MH/s should (on average)
+find a share every 11.3s
+If the last share was found more than 11.3 x 12 seconds (135.6s) ago,
+it is considered an error and highlighted
+If the last share was found more than 11.3 x 8 seconds (90.4s) ago,
+it is considered a warning and highlighted
+
+The default highlighting is very subtle
+
+---------
+
+Default:
+ $poolinputs = false;
+
+Set $poolinputs to true to show the input fields for adding a pool
+and changing the pool priorities on a single rig page
+However, if $readonly is true, it will not display them
+
+---------
+
+Default:
+ $rigport = 4028;
+
+Default port to use if any $rigs entries don't specify the port number
+
+---------
+
+Default:
+ $rigs = array('127.0.0.1:4028');
+
+Set $rigs to an array of your cgminer rigs that are running
+ format: 'IP' or 'Host' or 'IP:Port' or 'Host:Port' or 'Host:Port:Name'
+If you only have one rig, it will just show the detail of that rig
+If you have more than one rig it will show a summary of all the rigs
+ with buttons to show the details of each rig -
+ the button contents will be 'Name' rather than rig number, if you
+ specify 'Name'
+If Port is missing or blank, it will try $rigport
+e.g. $rigs = array('127.0.0.1:4028','myrig.com:4028:Sugoi');
+
+---------
+
+Default:
+ $rignames = false;
+
+Set $rignames to false to not affect the display.
+Set $rignames to one of 'ip' or 'ipx' to alter the name displayed
+if the rig doesn't have a 'name' in $rigs
+Currently:
+ 'ip' means use the 4th byte of the rig IP address as an integer
+ 'ipx' means use the 4th byte of the rig IP address as 2 hex bytes
+
+---------
+
+Default:
+ $rigbuttons = true;
+
+Set $rigbuttons to false to display a link rather than a button on
+the left of any summary table with rig buttons, in order to reduce
+the height of the table cells
+
+---------
+
+Default:
+ $mcast = false;
+
+Set $mcast to true to look for your rigs and ignore $rigs
+
+---------
+
+Default:
+ $mcastexpect = 0;
+
+The minimum number of rigs expected to be found when $mcast is true
+If fewer are found, an error will be included at the top of the page
+
+---------
+
+Default:
+ $mcastaddr = '224.0.0.75';
+
+API Multicast address all cgminers are listening on
+
+---------
+
+Default:
+ $mcastport = 4028;
+
+API Multicast UDP port all cgminers are listening on
+
+---------
+
+Default:
+ $mcastcode = 'FTW';
+
+The code all cgminers expect in the Multicast message sent
+The message sent is "cgm-code-listport"
+Don't use the '-' character if you change it
+
+---------
+
+Default:
+ $mcastlistport = 4027;
+
+UDP port number that is added to the broadcast message sent
+that specifies to the cgminers the port to reply on
+
+---------
+
+Default:
+ $mcasttimeout = 1.5;
+
+Set $mcasttimeout to the number of seconds (floating point)
+to wait for replies to the Multicast message
+N.B. the accuracy of the timing used to wait for the replies is
+~0.1s so there's no point making it more than one decimal place
+
+---------
+
+Default:
+ $mcastretries = 0;
+
+Set $mcastretries to the number of times to retry the multicast
+
+If $mcastexpect is 0, this is simply the number of extra times
+that it will send the multicast request
+N.B. cgminer doesn't listen for multicast requests for 1000ms after
+each one it hears
+
+If $mcastexpect is > 0, it will stop looking for replies once it
+has found at least $mcastexpect rigs, but it only checks this rig
+limit each time it reaches the $mcasttimeout limit, thus it can find
+more than $mcastexpect rigs if more exist
+It will send the multicast message up to $mcastretries extra times or
+until it has found at least $mcastexpect rigs
+However, when using $mcastretries, it is possible for it to sometimes
+ignore some rigs on the network if $mcastexpect is less than the
+number of rigs on the network and some rigs are too slow to reply
+
+---------
+
+Default:
+ $allowgen = false;
+
+Set $allowgen to true to allow customsummarypages to use 'gen' and 'bgen'
+false means ignore any 'gen' or 'bgen' options
+This is disabled by default due to the possible security risk of using it
+See the end of this document for an explanation
+
+---------
+
+Default:
+ $rigipsecurity = true;
+
+Set $rigipsecurity to false to show the IP/Port of the rig
+in the socket error messages and also show the full socket message
+
+---------
+
+Default:
+ $rigtotals = true;
+ $forcerigtotals = false;
+
+Set $rigtotals to true to display totals on the single rig page
+'false' means no totals (and ignores $forcerigtotals)
+
+If $rigtotals is true, all data is also right aligned
+With false, it's as before, left aligned
+
+This option is just here to allow people to set it to false
+if they prefer the old non-total display when viewing a single rig
+
+Also, if there is only one line shown in any section, then no
+total will be shown (to save screen space)
+You can force it to always show rig totals on the single rig page,
+even if there is only one line, by setting $forcerigtotals = true;
+
+---------
+
+Default:
+ $socksndtimeoutsec = 10;
+ $sockrcvtimeoutsec = 40;
+
+The numbers are integer seconds
+
+The defaults should be OK for most cases
+However, the longer SND is, the longer you have to wait while
+php hangs if the target cgminer isn't runnning or listening
+
+RCV should only ever be relevant if cgminer has hung but the
+API thread is still running, RCV would normally be >= SND
+
+Feel free to increase SND if your network is very slow
+or decrease RCV if that happens often to you
+
+Also, on some windows PHP, apparently the $usec is ignored
+(so usec can't be specified)
+
+---------
+
+Default:
+ $hidefields = array();
+
+List of fields NOT to be displayed
+You can use this to hide data you don't want to see or don't want
+shown on a public web page
+The list of sections are:
+ SUMMARY, POOL, PGA, GPU, NOTIFY, CONFIG, DEVDETAILS, DEVS
+See the web page for the list of field names (the table headers)
+It is an array of 'SECTION.Field Name' => 1
+
+This example would hide the slightly more sensitive pool information:
+Pool URL and pool username:
+ $hidefields = array('POOL.URL' => 1, 'POOL.User' => 1);
+
+If you just want to hide the pool username:
+ $hidefields = array('POOL.User' => 1);
+
+---------
+
+Default:
+ $ignorerefresh = false;
+ $changerefresh = true;
+ $autorefresh = 0;
+
+Auto-refresh of the page (in seconds) - integers only
+
+$ignorerefresh = true/false always ignore refresh parameters
+$changerefresh = true/false show buttons to change the value
+$autorefresh = default value, 0 means dont auto-refresh
+
+---------
+
+Default:
+ $miner_font_family = 'verdana,arial,sans';
+ $miner_font_size = '13pt';
+
+Change these to set the font and font size used on the web page
+
+---------
+
+Default:
+ $add_css_names = array();
+
+List of CSS names to add to the CSS style object
+	e.g. array('td.cool' => false);
+true/false to not include the default $miner_font
+The CSS name/value pairs must be defined in $colouroverride below
+
+This allows you to create multiple complete CSS styles, optionally
+using a different font to the default used/specified for all other
+styles, and then when using the class name in a custom formatting
+function (fmt) in a customsummarypage, it can use this style
+
+---------
+
+Default:
+ $colouroverride = array();
+
+Use this to change the web page colour scheme
+
+See $colourtable in miner.php for the list of possible names to change
+
+Simply put in $colouroverride, just the colours you wish to change
+
+e.g. to change the colour of the header font and background
+you could do the following:
+
+ $colouroverride = array(
+	'td.h color'		=> 'green',
+	'td.h background'	=> 'blue'
+ );
+
+You can also add your own CSS styles to be used by a customsummarypage
+custom format function, if you specify the class name in $add_css_names
+and put the class styles in $colouroverride
+
+---------
+
+Default:
+ $placebuttons = 'top';
+
+Where to place the Refresh, Summary, Custom Pages, Quit, etc. buttons
+
+Valid values are: 'top' 'bot' 'both'
+ anything else means don't show them - case sensitive
+
+---------
+
+Default:
+ $allowcustompages = true;
+
+Should we allow custom pages?
+(or just completely ignore them and don't display the buttons)
+
+---------
+
+OK this part is more complex: Custom Summary Pages
+
+A custom summary page in an array of 'section' => array('FieldA','FieldB'...)
+
+The section defines what data you want in the summary table and the Fields
+define what data you want shown from that section
+
+Standard sections are:
+ SUMMARY, POOL, PGA, GPU, NOTIFY, CONFIG, DEVDETAILS, DEVS, EDEVS, STATS,
+ ESTATS, COIN
+
+Fields are the names as shown on the headers on the normal pages
+
+There is a special field name '#' that will total to the number of rows
+displayed in the custom summary page
+In the actual row output it is a row counter per rig
+
+Fields can be 'name=new name' to display 'name' with a different heading
+'new name'
+
+There are also now joined sections:
+ SUMMARY+POOL, SUMMARY+DEVS, SUMMARY+EDEVS, DEVS+STATS, EDEVS+ESTATS,
+ POOL+STATS plus many more
+See the miner.php function joinsections() for the full list
+
+These sections are an SQL join of the two sections and the fields in them
+are named section.field where section. is the section the field comes from
+See the example further down
+
+Also note:
+- empty tables are not shown
+- empty columns (e.g. an unknown field) are not shown
+- missing field data shows as blank
+- the field name '*' matches all fields except in joined sections
+  (useful for STATS and COIN)
+
+There are 2 hard coded sections:
+ DATE - displays a date table like at the start of 'Summary'
+ RIGS - displays a rig table like at the start of 'Summary'
+
+Each custom summary requires a second array, that can be empty, listing fields
+to be totaled for each section
+If there is no matching total data, no total will show
+
+---------
+
+Looking at the Mobile example:
+
+ $mobilepage = array(
+  'DATE' => null,
+  'RIGS' => null,
+  'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', 
+			Accepted', 'Rejected=Rej', 'Utility'),
+  'DEVS+NOTIFY' => array('DEVS.Name=Name', 'DEVS.ID=ID', 'DEVS.Status=Status',
+			'DEVS.Temperature=Temp', 'DEVS.MHS av=MHS av',
+			'DEVS.Accepted=Accept', 'DEVS.Rejected=Rej',
+			'DEVS.Utility=Utility', 'NOTIFY.Last Not Well=Not Well'),
+  'POOL' => array('POOL', 'Status', 'Accepted', 'Rejected=Rej',
+                  'Last Share Time'));
+
+ $mobilesum = array(
+  'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted', 'Rejected',
+                     'Utility'),
+  'DEVS+NOTIFY' => array('DEVS.MHS av', 'DEVS.Accepted', 'DEVS.Rejected',
+                         'DEVS.Utility'),
+  'POOL' => array('Accepted', 'Rejected'));
+
+ $customsummarypages = array('Mobile' => array($mobilepage, $mobilesum));
+
+This will show 5 tables (according to $mobilepage)
+Each table will have the chosen details for all the rigs specified in $rigs
+
+ DATE
+	A single box with the web server's current date and time
+
+ RIGS
+	A table of the rigs: description, time, versions etc
+
+ SUMMARY
+
+	This will use the API 'summary' command and show the selected fields:
+		Elapsed, MHS av, Found Blocks, Accepted, Rejected and Utility
+	However, 'Rejected=Rej' means that the header displayed for the 'Rejected'
+	field will be 'Rej', instead of 'Rejected' (to save space)
+	Same for 'Found Blocks=Blks' - to save space
+
+ DEVS+NOTIFY
+
+	This will list each of the devices on each rig and display the list of
+	fields as shown
+	It will also include the 'Last Not Well' field from the 'notify' command
+	so you know when the device was last not well
+
+	You will notice that you need to rename each field e.g. 'DEVS.Name=Name'
+	since each field name in the join between DEVS and NOTIFY is actually
+	section.fieldname, not just fieldname
+
+	The join code automatically adds 2 fields to each GPU device: 'Name' and 'ID'
+	They don't exist in the API 'devs' output but I can correctly calculate
+	them from the GPU device data
+	These two fields are used to join DEVS to NOTIFY i.e. find the NOTIFY
+	record that has the same Name and ID as the DEVS record and join them
+
+ POOL
+
+	This will use the API 'pools' command and show the selected fields:
+		POOL, Status, Accepted, Rejected, Last Share Time
+	Again, I renamed the 'Rejected' field using 'Rejected=Rej', to save space
+
+$mobilesum lists the sections and fields that should have a total
+You can't define them for 'DATE' or 'RIGS' since they are hard coded tables
+The example given:
+
+ SUMMARY
+	Show a total at the bottom of the columns for:
+		MHS av, Found Blocks, Accepted, Rejected, Utility
+
+	Firstly note that you use the original name i.e. for 'Rejected=Rej'
+	you use 'Rejected', not 'Rej' and not 'Rejected=Rej'
+
+	Secondly note that it simply adds up the fields
+	If you ask for a total of a string field you will get the numerical
+	sum of the string data
+
+ DEVS+NOTIFY
+
+	Simply note in this join example that you must use the original field
+	names which are section.fieldname, not just fieldname
+
+ POOL
+	Show a total at the bottom of the columns for:
+		Accepted and Rejected
+
+	Again remember to use the original field name 'Rejected'
+
+---------
+
+With cgminer 2.10.2 and later, miner.php includes an extension to
+the custom pages that allows you to apply SQL style commands to
+the data: where, group, and having
+cgminer 3.4.2 and later also includes another option 'gen'
+cgminer 4.2.0 and later also includes another option 'fmt'
+cgminer 4.2.1 and later also includes another option 'bgen'
+
+An example of an 'ext' section in a more complex custom summary page:
+
+$poolsext = array(
+ 'POOL+STATS' => array(
+        'where' => null,
+        'group' => array('POOL.URL', 'POOL.Has Stratum',
+                         'POOL.Stratum Active', 'POOL.Has GBT'),
+        'calc' => array('POOL.Difficulty Accepted' => 'sum',
+                        'POOL.Difficulty Rejected' => 'sum',
+                        'STATS.Times Sent' => 'sum',
+                        'STATS.Bytes Sent' => 'sum',
+                        'STATS.Times Recv' => 'sum',
+                        'STATS.Bytes Recv' => 'sum'),
+        'gen' => array('AvShr', 'POOL.Difficulty Accepted/max(POOL.Accepted,1)),
+        'having' => array(array('STATS.Bytes Recv', '>', 0)),
+	'fmt' => 'myfmtfunc'));
+
+function myfmtfunc($section, $name, $value, $when, $alldata,
+		   $warnclass, $errorclass, $hiclass, $loclass, $totclass);
+{
+ $ret = '';
+ $class = '';
+ switch ($section.'.'.$name)
+ {
+ case 'GEN.AvShr':
+	$ret = number_format((float)$value, 2);
+	if ($value == 0)
+		$class = $errorclass;
+	break;
+ // Nonsence example :) since total would show the sum of the averages
+ case 'total.AvShr':
+	$ret = $value;
+	if ($value == 0)
+		$class = $warnclass;
+	break;
+ }
+ return array($ret, $class);
+}
+
+This allows you to group records together from one or more rigs
+In the example, you'll get each Pool (with the same URL+Stratum+GBT settings)
+listed once for all rigs and a sum of each of the fields listed in 'calc'
+
+
+'where' and 'having' are an array of fields and restrictions to apply
+
+In the above example, it will only display the rows where it contains the
+'STATS.Bytes Recv' field with a value greater than zero
+If the row doesn't have the field, it will always be included
+All restrictions must be true in order for the row to be included
+Any restiction that is invalid or unknown is true
+An empty array, or null, means there are no restrictions
+
+A restriction is formatted as: array('Field', 'restriction', 'value')
+Field is the simple field name as normally displayed, or SECTION.Field
+if it is a joined section (as in this case 'POOL+STATS')
+The list of restrictions are:
+'set' - true if the row contains the 'Field' ('value' is not required or used)
+'=', '<', '<=', '>', '>' - a numerical comparison
+'eq', 'lt', 'le', 'gt', 'ge' - a case insensitive string comparison
+
+You can have multiple restrictions on a 'Field' - but all must be true to
+include the row containing the 'Field'
+e.g. a number range between 0 and 10 would be:
+array('STATS.Bytes Recv', '>', 0), array('STATS.Bytes Recv', '<', 10)
+
+The difference between 'where' and 'having' is that 'where' is applied to the
+data before grouping it and 'having' is applied to the data after grouping it
+- otherwise they work the same
+
+
+'group' lists the fields to group over and 'calc' lists the function to apply
+to other fields that are not part of 'group'
+
+You can only see fields listed in 'group' and 'calc'
+
+A 'calc' is formatted as: 'Field' => 'function'
+The current list of operations available for 'calc' are:
+'sum', 'avg', 'min', 'max', 'lo', 'hi', 'count', 'any'
+The first 4 are as expected - the numerical sum, average, minimum or maximum
+'lo' is the first string of the list, sorted ignoring case
+'hi' is the last string of the list, sorted ignoring case
+'count' is the number of rows in the section specified in the calc e.g.
+ ('DEVS.Name' => 'count') would be the number of DEVS selected in the 'where'
+ of course any valid 'DEVS.Xyz' would give the same 'count' value
+'any' is effectively random: the field value in the 1st row of the grouped data
+An unrecognised 'function' uses 'any'
+
+
+A 'fmt' allows you to specify a function to be called by miner.php to format
+data to be displayed in the output html
+If the function doesn't exist in miner.php or myminer.php, then it will be
+ignored
+If the function returns a $ret value (see the example 'myfmtfunc' above) then
+that will be displayed, however if $ret is empty, then the normal formatting
+code will process the data to be displayed
+Thus, if there is no formatting code in miner.php for the field value, then it
+will be displayed as it was received from the API
+i.e. this allows you to either supply some php code to format field values
+that are not formatted by miner.php, or you can also override the formatting
+done by miner.php itself for your chosen list of field data
+You can return an '&nbsp;' if you wish to force it to display as blank
+Use the example 'myfmtfunc' above as a template to write your own
+Note that your provided function will be called for all data being displayed,
+so you should use the 'case' layout as in the example to select the data fields
+you wish to format, but return '' for fields you don't wish to change the way
+they are formatted
+The 2nd return field is the name of a CSS class in $colourtable or created in
+your own $add_css_names and $colouroverride
+The value you return can stay in effect even if you return an empty $ret, if
+the default formatting function for the field doesn't set the $class variable
+The fields passed to your function by miner.php:
+ $warnclass, $errorclass, $hiclass, $loclass, $totclass
+contain the default class names used for formatting
+
+
+A 'gen' or 'bgen' allows you to generate new fields from any php valid function
+of any of the other fields
+ e.g. 'gen' => array('AvShr', 'POOL.Difficulty Accepted/max(POOL.Accepted,1)),
+will generate a new field called GEN.AvShr that is the function shown, which
+in this case is the average difficulty of each share submitted
+
+The difference between 'bgen' and 'gen' is that 'bgen' is done before doing
+the 'group' and 'calc', however 'gen' is done after doing 'group' and 'calc'
+This means that 'group' and 'calc' can also use 'bgen' fields
+As before, 'gen' fields act on the results of the 'group' and 'calc'
+If there is no 'group' or 'calc' then they both will produce the same results
+Note that 'gen' fields are called 'GEN.field' and 'bgen' fields, 'BGEN.field'
+
+THERE IS A SECURITY RISK WITH HOW GEN/BGEN WORKS
+It simply replaces all the variables with their values and then requests PHP
+to execute the formula - thus if a field value returned from a cgminer API
+request contained PHP code, it could be executed by your web server
+Of course cgminer doesn't do this, but if you do not control the cgminer that
+returns the data in the API calls, someone could modify cgminer to return a
+PHP string in a field you use in 'gen' or 'bgen'
+Thus use 'gen' and 'bgen' at your own risk
+If someone feels the urge to write a mathematical interpreter in PHP to get
+around this risk, feel free to write one and submit it to the API author for
+consideration

BIN
API.class


+ 166 - 0
API.java

@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright (C) Andrew Smith 2012-2013
+ *
+ * Usage: java API command ip port
+ *
+ * If any are missing or blank they use the defaults:
+ *
+ *	command = 'summary'
+ *	ip	= '127.0.0.1'
+ *	port	= '4028'
+ *
+ */
+
+import java.net.*;
+import java.io.*;
+
+class API
+{
+	static private final int MAXRECEIVESIZE = 65535;
+
+	static private Socket socket = null;
+
+	private void closeAll() throws Exception
+	{
+		if (socket != null)
+		{
+			socket.close();
+			socket = null;
+		}
+	}
+
+	public void display(String result) throws Exception
+	{
+		String value;
+		String name;
+		String[] sections = result.split("\\|", 0);
+
+		for (int i = 0; i < sections.length; i++)
+		{
+			if (sections[i].trim().length() > 0)
+			{
+				String[] data = sections[i].split(",", 0);
+
+				for (int j = 0; j < data.length; j++)
+				{
+					String[] nameval = data[j].split("=", 2);
+
+					if (j == 0)
+					{
+						if (nameval.length > 1
+						&&  Character.isDigit(nameval[1].charAt(0)))
+							name = nameval[0] + nameval[1];
+						else
+							name = nameval[0];
+
+						System.out.println("[" + name + "] =>");
+						System.out.println("(");
+					}
+
+					if (nameval.length > 1)
+					{
+						name = nameval[0];
+						value = nameval[1];
+					}
+					else
+					{
+						name = "" + j;
+						value = nameval[0];
+					}
+
+					System.out.println("   ["+name+"] => "+value);
+				}
+				System.out.println(")");
+			}
+		}
+	}
+
+	public void process(String cmd, InetAddress ip, int port) throws Exception
+	{
+		StringBuffer sb = new StringBuffer();
+		char buf[] = new char[MAXRECEIVESIZE];
+		int len = 0;
+
+System.out.println("Attempting to send '"+cmd+"' to "+ip.getHostAddress()+":"+port);
+
+		try
+		{
+			socket = new Socket(ip, port);
+			PrintStream ps = new PrintStream(socket.getOutputStream());
+			ps.print(cmd.toCharArray());
+			ps.flush();
+
+			InputStreamReader isr = new InputStreamReader(socket.getInputStream());
+			while (0x80085 > 0)
+			{
+				len = isr.read(buf, 0, MAXRECEIVESIZE);
+				if (len < 1)
+					break;
+				sb.append(buf, 0, len);
+				if (buf[len-1] == '\0')
+					break;
+			}
+
+			closeAll();
+		}
+		catch (IOException ioe)
+		{
+			System.err.println(ioe.toString());
+			closeAll();
+			return;
+		}
+
+		String result = sb.toString();
+
+		System.out.println("Answer='"+result+"'");
+
+		display(result);
+	}
+
+	public API(String command, String _ip, String _port) throws Exception
+	{
+		InetAddress ip;
+		int port;
+
+		try
+		{
+			ip = InetAddress.getByName(_ip);
+		}
+		catch (UnknownHostException uhe)
+		{
+			System.err.println("Unknown host " + _ip + ": " + uhe);
+			return;
+		}
+
+		try
+		{
+			port = Integer.parseInt(_port);
+		}
+		catch (NumberFormatException nfe)
+		{
+			System.err.println("Invalid port " + _port + ": " + nfe);
+			return;
+		}
+
+		process(command, ip, port);
+	}
+
+	public static void main(String[] params) throws Exception
+	{
+		String command = "summary";
+		String ip = "127.0.0.1";
+		String port = "4028";
+
+		if (params.length > 0 && params[0].trim().length() > 0)
+			command = params[0].trim();
+
+		if (params.length > 1 && params[1].trim().length() > 0)
+			ip = params[1].trim();
+
+		if (params.length > 2 && params[2].trim().length() > 0)
+			port = params[2].trim();
+
+		new API(command, ip, port);
+	}
+}

+ 860 - 0
ASIC-README

@@ -0,0 +1,860 @@
+SUPPORTED DEVICES
+
+Currently supported devices include:
+- Antminer U1/U2/U2+/U3 USB
+- Antminer S1
+- ASICMINER block erupters
+- ASICMINER Tube/Prisma
+- Avalon (including BitBurner and Klondike)
+- Avalon2/3
+- Avalon4/4.1/6
+- Avalon7
+- Avalon8
+- BFx2 USB
+- Butterfly Labs SC 65/28nm range
+- BF1 (bitfury) USB (red and blue)
+- BlackArrow Bitfury
+- BlackArrow Minion
+- Bi*fury USB
+- Cointerra
+- Compac bitshopperde
+- GekkoScience
+- Halong Dragonmint T1
+- Hashfast Babyjet and Sierra
+- Hashratio
+- Hexfury USB
+- KnCminer Mercury, Saturn and Jupiter
+- Nanofury USB
+- Other bitfury USB devices
+- Onestring miner USB
+- Rockminer R-Box/RK-Box/T1/New R-Box
+- Spondoolies SP10, SP30
+
+
+No COM ports on windows or TTY devices will be used by cgminer as it
+communicates directly with them via USB so it is normal for them to not exist or
+be disconnected when cgminer is running.
+
+The BFL devices should come up as one of the following:
+
+BAJ: BFL ASIC Jalapeño
+BAL: BFL ASIC Little Single
+BAS: BFL ASIC Single
+BAM: BFL ASIC Minirig
+BMA: BFL Monarch
+
+BFL devices need the --enable-bflsc option when compiling cgminer yourself.
+
+Avalon will come up as AVA.
+
+Avalon devices need the --enable-avalon option when compiling cgminer.
+
+Avalon2/3 will come up as AV2.
+
+Avalon2/3 devices need the --enable-avalon2 option when compiling cgminer.
+
+Avalon4/4.1 will come up as AV4, Please use the MM version to distinguish.
+
+Avalon6 will come up as AV6.
+
+Avalon4/4.1/6 devies need the --enable-avalon4 option when compiling cgminer.
+
+Avalon7 will come up as AV7.
+
+Avalon8 will come up as AV8.
+
+Klondike will come up as KLN.
+
+Klondike devices need the --enable-klondike option when compiling cgminer.
+
+ASICMINER block erupters will come up as AMU.
+
+ASICMINER devices need the --enable-icarus option when compiling cgminer.
+Also note that the AMU is managed by the Icarus driver which is detailed
+in the FPGA-README. Configuring them uses the same mechanism as outlined
+below for getting started with USB ASICs.
+
+ASICMINER BlockErupter Tube/Prisma will come up as BET.
+
+ASICMINER Tube/Prisma devices need the --enable-blockerupter option when
+compiling cgminer. This driver is apparently BROKEN and the original contributor
+has not submitted fixes.
+
+BlackArrow Bitfury devices
+
+BlackArrow Bitfury devices need the --enable-bab option when compiling cgminer.
+
+The current BlackArrow Bitfury devices are similar to the Bitfury GPIO mining
+boards, with both V1 and V2 controllers, and come up as BaB.
+
+
+BlackArrow Minion devices
+
+BlackArrow Minion devices need the --enable-minion option when compiling
+cgminer.
+
+BlackArrow Minion devices are SPI/GPIO mining devices and come up as MBA
+
+
+BITFURY devices
+
+Bitfury devices need the --enable-bitfury option when compiling cgminer.
+
+Currently the BPMC/BGMC BF1 devices AKA redfury/bluefury are supported and
+come up as BF1, along with the Bi*fury USB devices which come up as BXF.
+Nanofury devices come up as NF1. BFx2 devices come up as BXM.
+
+Bitfury USB devices are also set up as per the USB ASICs below.
+
+
+COINTERRA devices
+
+Cointerra devices need the --enable-cointerra option when compiling cgminer.
+
+Cointerra devices come up as CTA devices and currently take only hidden command
+line arguments for power settings.
+
+Cointerra USB devices are set up as per the USB ASIC instructions below.
+
+
+Compac devices
+
+Bitshopperde compac devices need the --enable-gekko option when compiling cgminer.
+This also supports the GekkoScience devices.
+
+bitshopperde come up as BSC, BSD, BSE
+
+
+GekkoScience devices
+
+GekkoScience devices need the --enable-gekko option when compiling cgminer.
+This also supports the bitshopperde devices.
+
+GekkoScience come up as GSC, GSD, GSE, GSF, GSH, GSI
+
+
+HALONG devices
+
+HALONG Dragonmint T1 devices will come up as DT1.
+
+Halong devices need the --enable-dragonmint_t1 option.
+
+
+
+
+HASHFAST devices
+
+Hashfast devices need the --enable-hashfast option when compiling cgminer.
+
+All current HFA devices are supported and are recognised with the name HFA
+in the --usb commands. After initialisation, cgminer will determine what type
+they are and give them the following names:
+
+HFB: Hashfast Babyjet
+HFS: Hashfast Sierra
+HFA: Hashfast non standard (eg. a Babyjet with an added board, Habanero)
+
+
+HASHRATIO devices
+
+Hashratio devices need the --enable-hashratio option when compiling cgminer.
+
+
+ANTMINER U1/U2+/U3 devices
+
+Antminer devices need the --enable-icarus option when compiling cgminer.
+
+Currently the U1/2/3 USB sticks are supported and come up as the following
+devices:
+
+ANU: Antminer U1/U2/U2+
+AU3: Antminer U3
+
+They are also set up as per the USB ASICs below.
+
+ANTMINER S1 devices
+
+Antminer S1 devices need the --enable-ants1 option when compiling cgminer.
+
+They are custom OpenWRT linux devices
+
+They are recognised with the name ANT
+
+
+BITMINE A1 devices
+
+Bitmine A1 devices need the --enable-bitmine_A1 compile option set.
+
+
+Rockminer R*Box
+
+Rockminer R*Box devices need the --enable-icarus compile option set.
+
+They appear with the following names:
+LIN: R-Box
+LIR: New R-Box
+
+---
+GETTING STARTED WITH USB ASICS
+
+Unlike other software, cgminer uses direct USB communication instead of the
+ancient serial USB communication to be much faster, more reliable and use a
+lot less CPU. For this reason, setting up for mining with cgminer on these
+devices requires different drivers.
+
+
+See README 'SETTING UP USB DEVICES' for setting up USB mining with cgminer
+
+---
+
+ASIC SPECIFIC COMMANDS
+
+--anu-freq <arg>    Set AntminerU1/2 frequency in MHz, range 125-500 (default: 250.0)
+--au3-freq <arg>    Set AntminerU3 frequency in MHz, range 100-250 (default: 225.0)
+--au3-volt <arg>    Set AntminerU3 voltage in mv, range 725-850, 0 to not set (default: 775)
+--avalon-auto       Adjust avalon overclock frequency dynamically for best hashrate
+--avalon-cutoff <arg> Set avalon overheat cut off temperature (default: 60)
+--avalon-fan <arg> Set fanspeed percentage for avalon, single value or range (default: 20-100)
+--avalon-freq <arg> Set frequency range for avalon-auto, single value or range
+--avalon-options <arg> Set avalon options baud:miners:asic:timeout:freq:tech
+--avalon-temp <arg> Set avalon target temperature (default: 50)
+--avalon2-freq      Set frequency range for Avalon2, single value or range
+--avalon2-voltage   Set Avalon2 core voltage, in millivolts
+--avalon2-fan       Set Avalon2 target fan speed
+--avalon2-cutoff <arg> Set Avalon2 overheat cut off temperature (default: 88)
+--avalon2-fixed-speed Set Avalon2 fan to fixed speed
+--avalon4-automatic-voltage Automatic adjust voltage base on module DH
+--avalon4-voltage   Set Avalon4 core voltage, in millivolts, step: 125
+--avalon4-freq      Set frequency for Avalon4, 1 to 3 values, example: 445:385:370
+--avalon4-fan       Set Avalon4 target fan speed range
+--avalon4-temp <arg> Set Avalon4 target temperature (default: 42)
+--avalon4-cutoff <arg> Set Avalon4 overheat cut off temperature (default: 65)
+--avalon4-polling-delay <arg> Set Avalon4 polling delay value (ms) (default: 20)
+--avalon4-ntime-offset <arg> Set Avalon4 MM ntime rolling max offset (default: 4)
+--avalon4-aucspeed <arg> Set Avalon4 AUC IIC bus speed (default: 400000)
+--avalon4-aucxdelay <arg> Set Avalon4 AUC IIC xfer read delay, 4800 ~= 1ms (default: 9600)
+--avalon4-miningmode <arg> Set Avalon4 mining mode(0:custom, 1:eco, 2:normal, 3:turbo (default: 0)
+--avalon4-freezesafe Make Avalon4 running as a radiator when stratum server failed
+--avalon4-ntcb <arg> Set Avalon4 MM NTC B value (default: 3450)
+--avalon4-freq-min <arg> Set minimum frequency for Avalon4 (default: 100)
+--avalon4-freq-max <arg> Set maximum frequency for Avalon4 (default: 1000)
+--avalon4-noncecheck-off Disable A3218 inside nonce check function
+--avalon4-smart-speed <arg> Set smart speed, range 0-3. 0 means Disable (default: 2)
+--avalon4-speed-bingo <arg> Set A3218 speed bingo for smart speed mode 1 (default: 255)
+--avalon4-speed-error <arg> Set A3218 speed error for smart speed mode 1 (default: 3)
+--avalon4-least-pll <arg> Set least pll check threshold for smart speed mode 2 (default: 768)
+--avalon4-most-pll <arg> Set most pll check threshold for smart speed mode 2 (default: 256)
+--avalon7-voltage   Set Avalon7 default core voltage, in millivolts, step: 78
+--avalon7-voltage-level Set Avalon7 default level of core voltage, range:[0, 15], step: 1
+--avalon7-voltage-offset Set Avalon7 default offset of core voltage, range:[-2, 1], step: 1
+--avalon7-freq      Set Avalon7 default frequency, range:[24, 1404], step: 12, example: 500
+--avalon7-freq-sel <arg> Set Avalon7 default frequency select, range:[0, 5], step: 1, example: 3 (default: 0)
+--avalon7-fan       Set Avalon7 target fan speed, range:[0, 100], step: 1, example: 0-100
+--avalon7-temp <arg> Set Avalon7 target temperature, range:[0, 100] (default: 99)
+--avalon7-polling-delay <arg> Set Avalon7 polling delay value (ms) (default: 20)
+--avalon7-aucspeed <arg> Set AUC3 IIC bus speed (default: 400000)
+--avalon7-aucxdelay <arg> Set AUC3 IIC xfer read delay, 4800 ~= 1ms (default: 19200)
+--avalon7-smart-speed <arg> Set Avalon7 smart speed, range 0-1. 0 means Disable (default: 1)
+--avalon7-th-pass <arg> Set A3212 th pass value (default: 162)
+--avalon7-th-fail <arg> Set A3212 th fail value (default: 10921)
+--avalon7-th-init <arg> Set A3212 th init value (default: 32767)
+--avalon7-th-ms <arg> Set A3212 th ms value (default: 1)
+--avalon7-th-timeout <arg> Set A3212 th timeout value (default: 0)
+--avalon7-iic-detect Enable Avalon7 detect through iic controller
+--avalon7-freqadj-time <arg> Set Avalon7 check interval when run in AVA7_FREQ_TEMPADJ_MODE (default: 60)
+--avalon7-delta-temp <arg> Set Avalon7 delta temperature when reset freq in AVA7_FREQ_TEMPADJ_MODE (default: 0)
+--avalon7-delta-freq <arg> Set Avalon7 delta freq when adjust freq in AVA7_FREQ_TEMPADJ_MODE (default: 100)
+--avalon7-freqadj-temp <arg> Set Avalon7 check temperature when run into AVA7_FREQ_TEMPADJ_MODE (default: 104)
+--avalon7-nonce-mask <arg> Set A3212 nonce mask, range 24-32. (default: 31)
+--no-avalon7-asic-debug Disable A3212 debug.
+--avalon8-voltage-level Set Avalon8 default level of core voltage, range:[0, 15], step: 1
+--avalon8-voltage-level-offset Set Avalon8 default offset of core voltage level, range:[-2, 1], step: 1
+--avalon8-freq      Set Avalon8 default frequency, range:[25, 1200], step: 25, example: 800
+--avalon8-freq-sel <arg> Set Avalon8 default frequency select, range:[0, 3], step: 1, example: 3 (default: 3)
+--avalon8-fan       Set Avalon8 target fan speed, range:[0, 100], step: 1, example: 0-100
+--avalon8-temp <arg> Set Avalon8 target temperature, range:[0, 100] (default: 90)
+--avalon8-polling-delay <arg> Set Avalon8 polling delay value (ms) (default: 20)
+--avalon8-aucspeed <arg> Set AUC3 IIC bus speed (default: 400000)
+--avalon8-aucxdelay <arg> Set AUC3 IIC xfer read delay, 4800 ~= 1ms (default: 19200)
+--avalon8-smart-speed <arg> Set Avalon8 smart speed, range 0-1. 0 means Disable (default: 1)
+--avalon8-th-pass <arg> Set A3210 th pass value (default: -1)
+--avalon8-th-fail <arg> Set A3210 th fail value (default: -1)
+--avalon8-th-init <arg> Set A3210 th init value (default: 32767)
+--avalon8-th-ms <arg> Set A3210 th ms value (default: 5)
+--avalon8-th-timeout <arg> Set A3210 th timeout value (default: 4294967295)
+--avalon8-th-add <arg> Set A3210 th add value (default: 1)
+--avalon8-iic-detect Enable Avalon8 detect through iic controller
+--avalon8-nonce-mask <arg> Set A3210 nonce mask, range 24-32. (default: -1)
+--avalon8-nonce-check <arg> Set A3210 nonce check, range 0-1. (default: 1)
+--avalon8-roll-enable <arg> Set A3210 roll enable, range 0-1. (default: 1)
+--avalon8-mux-l2h <arg> Set Avalon8 mux l2h, range 0-2. (default: 0)
+--avalon8-mux-h2l <arg> Set Avalon8 mux h2l, range 0-1. (default: 1)
+--avalon8-h2ltime0-spd <arg> Set Avalon8 h2ltime0 spd, range 0-255. (default: 3)
+--avalon8-spdlow <arg> Set Avalon8 spdlow, range 0-3. (default: -1)
+--avalon8-spdhigh <arg> Set Avalon8 spdhigh, range 0-3. (default: 3)
+--avalon8-cinfo-asic Set Avalon8 cinfo asic index, range:[0, 25], step: 1
+--avalon8-pid-p <arg> Set Avalon8 pid-p, range 0-9999. (default: 2)
+--avalon8-pid-i <arg> Set Avalon8 pid-i, range 0-9999. (default: 5)
+--avalon8-pid-d <arg> Set Avalon8 pid-d, range 0-9999. (default: 0)
+--bab-options <arg> Set BaB options max:def:min:up:down:hz:delay:trf
+--bet-clk <arg>     Set clockspeed of ASICMINER Tube/Prisma to (arg+1)*10MHz (default: 23)
+--bflsc-overheat <arg> Set overheat temperature where BFLSC devices throttle, 0 to disable (default: 90)
+--bitburner-fury-options <arg> Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq
+--bitburner-fury-voltage <arg> Set BitBurner Fury core voltage, in millivolts
+--bitburner-voltage <arg> Set BitBurner (Avalon) core voltage, in millivolts
+--bitmain-auto      Adjust bitmain overclock frequency dynamically for best hashrate
+--bitmain-cutoff <arg> Set bitmain overheat cut off temperature
+--bitmain-fan <arg> Set fanspeed percentage for bitmain, single value or range (default: 20-100)
+--bitmain-freq <arg> Set bitmain freq options timeout:freq:regdata
+--bitmain-hwerror   Set bitmain device detect hardware error
+--bitmain-options <arg> Set bitmain options baud:miners:asic:timeout:freq:regdata
+--bitmain-temp <arg> Set bitmain target temperature
+--bitmain-workdelay <arg> Set bitmain work delay (ms) 0-100
+--bitmain-voltage <arg> Set bitmain voltage - S2/S3 only
+--bitmain-dev <arg> Set bitmain device - S2 only
+--bitmainbeeper     Set bitmain beeper ringing
+--bitmaintempoverctrl Set bitmain stop runing when temprerature is over 80 degree Celsius
+--bxf-bits <arg>    Set max BXF/HXF bits for overclocking (default: 54)
+--bxf-temp-target <arg> Set target temperature for BXF/HXF devices (default: 82)
+--bxm-bits <arg>    Set BXM bits for overclocking (default: 54)
+--hfa-hash-clock <arg> Set hashfast clock speed (default: 550)
+--hfa-fail-drop <arg> Set how many MHz to drop clockspeed each failure on an overlocked hashfast device (default: 10)
+--hfa-fan <arg>     Set fanspeed percentage for hashfast, single value or range (default: 10-85)
+--hfa-name <arg>    Set a unique name for a single hashfast device specified with --usb or the first device found
+--hfa-noshed        Disable hashfast dynamic core disabling feature
+--hfa-options <arg> Set hashfast options name:clock or name:clock@voltage (comma separated)
+--hfa-temp-overheat <arg> Set the hashfast overheat throttling temperature (default: 95)
+--hfa-temp-target <arg> Set the hashfast target temperature (0 to disable) (default: 88)
+--hro-freq          Set the hashratio clock frequency (default: 280)
+--klondike-options <arg> Set klondike options clock:temptarget
+--minion-chipreport <arg> Seconds to report chip 5min hashrate, range 0-100 (default: 0=disabled)
+--minion-freq <arg> Set minion chip frequencies in MHz, single value or comma list, range 100-1400 (default: 1200)
+--minion-freqchange Millisecond total time to do frequency changes (default: 1000)
+--minion-freqpercent Percentage to use when starting up a chip (default: 70%)
+--minion-idlecount  Report when IdleCount is >0 or changes
+--minion-ledcount   Turn off led when more than this many chips below the ledlimit (default: 0)
+--minion-ledlimit   Turn off led when chips GHs are below this (default: 90)
+--minion-idlecount  Report when IdleCount is >0 or changes
+--minion-noautofreq Disable automatic frequency adjustment
+--minion-overheat   Enable directly halting any chip when the status exceeds 100C
+--minion-spidelay   Add a delay in microseconds after each SPI I/O
+--minion-spireset   SPI regular reset: iNNN for I/O count or sNNN for seconds - 0 means none
+--minion-spisleep   Sleep time in milliseconds when doing an SPI reset
+--minion-temp <arg> Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)
+--nfu-bits <arg>    Set nanofury bits for overclocking, range 32-63 (default: 50)
+--rock-freq <arg>   Set RockMiner frequency in MHz, range 125-500 (default: 270)
+
+
+ANTMINER S1 DEVICES
+
+--bitmain-auto      Adjust bitmain overclock frequency dynamically for best hashrate
+--bitmain-cutoff    Set bitmain overheat cut off temperature
+--bitmain-fan       Set fanspeed percentage for bitmain, single value or range (default: 20-100)
+--bitmain-freq      Set frequency range for bitmain-auto, single value or range
+--bitmain-hwerror   Set bitmain device detect hardware error
+--bitmain-options   Set bitmain options baud:miners:asic:timeout:freq
+--bitmain-temp      Set bitmain target temperature
+
+The Antminer S1 device comes with its own operating system and a preinstalled
+version of cgminer as part of the flash firmware. No configuration should be
+necessary.
+
+
+ANTMINER U1/2/3 DEVICES
+
+--anu-freq <arg>    Set AntminerU1 frequency in MHz, range 150-500 (default: 200)
+--au3-freq <arg>    Set AntminerU3 frequency in MHz, range 100-250 (default: 225.0)
+--au3-volt <arg>    Set AntminerU3 voltage in mv, range 725-850, 0 to not set (default: 750)
+
+By default, Antminer U1 devices run at a clockspeed of 200. This command allows
+you to specify a chosen frequency to attempt to run all ANU devices at. Cgminer
+will try to find the nearest frequency the device supports and will report if
+the frequency is not exactly as requested. Note that cgminer reports hashrate
+ONLY FROM VALID HASHES so if you increase the frequency but your hashrate does
+not increase or it decreases and hardware errors start showing up, you have
+overclocked it too much. In the worst case scenario it will fail to start at too
+high a speed. Most will run happily up to 250.
+
+ASICMINER BlockErupter Tube/Prisma DEVICES
+
+--bet-clk <arg>     Set clockspeed of ASICMINER Tube/Prisma to (arg+1)*10MHz (default: 23)
+
+Default clockspeed for Tube/Prisma is 240MHz. This command allows to set clockspeed
+of on board BE200 chips in range from 200MHz to 320MHz. For Tube devices, you can
+try overclocking to 270MHz or even higher, but NOT recommended for Prisma devices.
+If you notice hash rate drops or board fails to start, restart cgminer with lower
+clockspeed.
+
+
+AVALON AND BITBURNER DEVICES
+
+Currently all known Avalon devices come with their own operating system and
+a preinstalled version of cgminer as part of the flash firmware, based on the
+most current cgminer version so no configuration should be necessary. It is
+possible to plug a USB cable from a PC into the Avalon device and mine using
+cgminer as per any other device. It will autodetect and hotplug using default
+options. You can customise the avalon behaviour by using the avalon-options
+command, and adjust its fan control-temperature relationship with avalon-temp.
+By default the avalon will also cut off when its temperature reaches 60
+degrees.
+
+All current BitBurner devices (BitBurner X, BitBurner XX and BitBurner Fury)
+emulate Avalon devices, whether or not they use Avalon chips.
+
+Avalon commands:
+
+--avalon-auto       Adjust avalon overclock frequency dynamically for best hashrate
+--avalon-cutoff <arg> Set avalon overheat cut off temperature (default: 60)
+--avalon-fan <arg> Set fanspeed percentage for avalon, single value or range (default: 20-100)
+--avalon-freq <arg> Set frequency range for avalon-auto, single value or range
+--avalon-options <arg> Set avalon options baud:miners:asic:timeout:freq:tech
+--avalon-temp <arg> Set avalon target temperature (default: 50)
+--bitburner-fury-options <arg> Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq
+--bitburner-fury-voltage <arg> Set BitBurner Fury core voltage, in millivolts
+--bitburner-voltage <arg> Set BitBurner (Avalon) core voltage, in millivolts
+
+
+Avalon auto will enable dynamic overclocking gradually increasing and
+decreasing the frequency till the highest hashrate that keeps hardware errors
+under 2% is achieved. This WILL run your avalon beyond its normal specification
+so the usual warnings apply. When avalon-auto is enabled, the avalon-options
+for frequency and timeout are used as the starting point only.
+
+eg:
+--avalon-fan 50
+--avalon-fan 40-80
+
+By default the avalon fans will be adjusted to maintain a target temperature
+over a range from 20 to 100% fanspeed. avalon-fan allows you to limit the
+range of fanspeeds to a single value or a range of values.
+
+eg:
+--avalon-freq 300-350
+
+In combination with the avalon-auto command, the avalon-freq command allows you
+to limit the range of frequencies which auto will adjust to.
+
+eg:
+--avalon-temp 55
+
+This will adjust fanspeed to keep the temperature at or slightly below 55.
+If you wish the fans to run at maximum speed, setting the target temperature
+very low such as 0 will achieve this. This option can be added to the "More
+options" entry in the web interface if you do not have a direct way of setting
+it.
+
+eg:
+--avalon-cutoff 65
+
+This will cut off the avalon should it get up to 65 degrees and will then
+re-enable it when it gets to the target temperature as specified by avalon-temp.
+
+eg:
+--avalon-options 115200:24:10:D:1500:55
+
+The values are baud : miners : asic count : timeout : frequency : technology.
+
+Baud:
+The device is pretty much hard coded to emulate 115200 baud so you shouldn't
+change this.
+
+Miners:
+Most Avalons are 3 module devices, which come to 24 miners. 4 module devices
+would use 32 here.
+
+For BitBurner X and BitBurner XX devices you should use twice the number of
+boards in the stack.  e.g. for a two-board stack you would use 4.  For
+BitBurner Fury devices you should use the total number of BitFury chips in the
+stack (i.e. 16 times the number of boards).  e.g. for a two-board stack you
+would use 32.
+
+Asic count:
+Virtually all have 10, so don't change this.  BitBurner devices use 10 here
+even if the boards have some other number of ASICs.
+
+Timeout:
+This is how long the device will work on a work item before accepting new work
+to replace it. It should be changed according to the frequency (last setting).
+It is possible to set this a little lower if you are trying to tune for short
+block mining (eg p2pool) but much lower and the device will start creating
+duplicate shares.
+A value of 'd' means cgminer will calculate it for you based on the frequency
+and is highly recommended.
+
+Sample settings for valid different frequencies (last 3 values) for 110nm AVAs:
+34:375:110 *
+36:350:110 *
+43:300:110
+45:282:110 (default)
+50:256:110
+
+Note that setting a value with an asterisk next to it will be using your
+avalon outside its spec and you do so at your own risk.
+
+For 55nm AVAs, the usual values are 8:1500
+
+Frequency:
+This is the clock speed of the devices. For Avalon 110nm devices, values from
+256 upwards are valid with the default being 282 and the maximum practical
+being approximately 350. For 55nm devices values from 1000-2000 are valid with
+1500 being the default.
+
+Technology:
+What sized technology ASICs are in use in the avalon, choices are 55 or 110,
+corresponding to the nm technology chips in use.
+
+The default frequency for BitBurner X and BitBurner XX boards is 282.  The
+default frequency for BitBurner Fury boards is 256.  Overclocking is
+possible - please consult the product documentation and/or manufacturer for
+information on safe values.  Values outside this range are used at your own
+risk.  Underclocking is also possible, at least with the X and XX boards.
+
+eg:
+--bitburner-fury-options <arg> Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq
+
+This option takes the same format as --avalon-options.  When specified, it
+will be used for BitBurner Fury boards in preference to the values specified
+in --avalon-options.  (If not specified, BitBurner Fury boards will be
+controlled by the values used in --avalon options.)  See --avalon-options for
+a detailed description of the fields.
+
+This option is particularly useful when using a mixture of different BitBurner
+devices as BitBurner Fury devices generally require significantly different
+clock frequencies from Avalon-based devices.  This option is only available
+for boards with recent firmware that are recognized by cgminer as BBF.
+
+eg:
+--bitburner-fury-voltage <arg> Set BitBurner Fury core voltage, in millivolts
+
+Sets the core voltage for the BitBurner Fury boards.  The default value is
+900.  Overvolting is possible - please consult the product documentation
+and/or manufaturer about the safe range of values.  Values outside this range
+are used at your own risk.
+
+This option is only available for boards with recent firmware that are
+recognized by cgminer as BBF.  For boards recognized as BTB, see
+--bitburner-voltage
+
+eg:
+--bitburner-voltage <arg> Set BitBurner (Avalon) core voltage, in millivolts
+
+Sets the core voltage for the Avalon-based BitBurner X and BitBurner XX
+boards.  The default value is 1200.  Overvolting and undervolting is
+possible - please consult the product documentation and/or the manufacturer
+for information about the safe range.  Values outside this range are used at
+your own risk.
+
+Older BitBurner Fury firmware emulates a BitBurner XX board and is identified
+by cgminer as BTB.  On these devices, --bitburner-voltage is used to control
+the voltage of the BitBurner Fury board.  The actual core voltage will be
+300mV less than the requested voltage, so to run a BitBurner Fury board at
+950mV use --bitburner-voltage 1250.  The default value of 1200 therefore
+corresponds to the default core voltage of 900mV.
+
+
+If you use the full curses based interface with Avalons you will get this
+information:
+AVA 0: 22/ 46C  2400R
+
+The values are:
+ambient temp / highest device temp  lowest detected ASIC cooling fan RPM.
+
+Use the API for more detailed information than this.
+
+
+Avalon2 Devices
+
+--avalon2-freq      Set frequency range for Avalon2, single value or range
+--avalon2-voltage   Set Avalon2 core voltage, in millivolts
+--avalon2-fan       Set Avalon2 target fan speed
+--avalon2-cutoff <arg> Set Avalon2 overheat cut off temperature (default: 88)
+--avalon2-fixed-speed Set Avalon2 fan to fixed speed
+
+
+Avalon4 Devices
+
+--avalon4-automatic-voltage Automatic adjust voltage base on module DH
+--avalon4-voltage   Set Avalon4 core voltage, in millivolts, step: 125
+--avalon4-freq      Set frequency for Avalon4, 1 to 3 values, example: 445:385:370
+--avalon4-fan       Set Avalon4 target fan speed range
+--avalon4-temp <arg> Set Avalon4 target temperature (default: 42)
+--avalon4-cutoff <arg> Set Avalon4 overheat cut off temperature (default: 65)
+--avalon4-polling-delay <arg> Set Avalon4 polling delay value (ms) (default: 20)
+--avalon4-ntime-offset <arg> Set Avalon4 MM ntime rolling max offset (default: 4)
+--avalon4-aucspeed <arg> Set Avalon4 AUC IIC bus speed (default: 400000)
+--avalon4-aucxdelay <arg> Set Avalon4 AUC IIC xfer read delay, 4800 ~= 1ms (default: 9600)
+--avalon4-miningmode <arg> Set Avalon4 mining mode(0:custom, 1:eco, 2:normal, 3:turbo (default: 0)
+--avalon4-freezesafe Make Avalon4 running as a radiator when stratum server failed
+--avalon4-ntcb <arg> Set Avalon4 MM NTC B value (default: 3450)
+--avalon4-freq-min <arg> Set minimum frequency for Avalon4 (default: 100)
+--avalon4-freq-max <arg> Set maximum frequency for Avalon4 (default: 1000)
+--avalon4-noncecheck-off Disable A3218 inside nonce check function
+--avalon4-smart-speed <arg> Set smart speed, range 0-3. 0 means Disable (default: 2)
+--avalon4-speed-bingo <arg> Set A3218 speed bingo for smart speed mode 1 (default: 255)
+--avalon4-speed-error <arg> Set A3218 speed error for smart speed mode 1 (default: 3)
+--avalon4-least-pll <arg> Set least pll check threshold for smart speed mode 2 (default: 768)
+--avalon4-most-pll <arg> Set most pll check threshold for smart speed mode 2 (default: 256)
+
+Avalon7 Devices
+
+--avalon7-voltage   Set Avalon7 default core voltage, in millivolts, step: 78
+--avalon7-voltage-level Set Avalon7 default level of core voltage, range:[0, 15], step: 1
+--avalon7-voltage-offset Set Avalon7 default offset of core voltage, range:[-2, 1], step: 1
+--avalon7-freq      Set Avalon7 default frequency, range:[24, 1404], step: 12, example: 500
+--avalon7-freq-sel <arg> Set Avalon7 default frequency select, range:[0, 5], step: 1, example: 3 (default: 0)
+--avalon7-fan       Set Avalon7 target fan speed, range:[0, 100], step: 1, example: 0-100
+--avalon7-temp <arg> Set Avalon7 target temperature, range:[0, 100] (default: 99)
+--avalon7-polling-delay <arg> Set Avalon7 polling delay value (ms) (default: 20)
+--avalon7-aucspeed <arg> Set AUC3 IIC bus speed (default: 400000)
+--avalon7-aucxdelay <arg> Set AUC3 IIC xfer read delay, 4800 ~= 1ms (default: 19200)
+--avalon7-smart-speed <arg> Set Avalon7 smart speed, range 0-1. 0 means Disable (default: 1)
+--avalon7-th-pass <arg> Set A3212 th pass value (default: 162)
+--avalon7-th-fail <arg> Set A3212 th fail value (default: 10921)
+--avalon7-th-init <arg> Set A3212 th init value (default: 32767)
+--avalon7-th-ms <arg> Set A3212 th ms value (default: 1)
+--avalon7-th-timeout <arg> Set A3212 th timeout value (default: 0)
+--avalon7-iic-detect Enable Avalon7 detect through iic controller
+--avalon7-freqadj-time <arg> Set Avalon7 check interval when run in AVA7_FREQ_TEMPADJ_MODE (default: 60)
+--avalon7-delta-temp <arg> Set Avalon7 delta temperature when reset freq in AVA7_FREQ_TEMPADJ_MODE (default: 0)
+--avalon7-delta-freq <arg> Set Avalon7 delta freq when adjust freq in AVA7_FREQ_TEMPADJ_MODE (default: 100)
+--avalon7-freqadj-temp <arg> Set Avalon7 check temperature when run into AVA7_FREQ_TEMPADJ_MODE (default: 104)
+--avalon7-nonce-mask <arg> Set A3212 nonce mask, range 24-32. (default: 31)
+--no-avalon7-asic-debug Disable A3212 debug.
+
+Avalon8 Devices
+
+--avalon8-voltage-level Set Avalon8 default level of core voltage, range:[0, 15], step: 1
+--avalon8-voltage-level-offset Set Avalon8 default offset of core voltage level, range:[-2, 1], step: 1
+--avalon8-freq      Set Avalon8 default frequency, range:[25, 1200], step: 25, example: 800
+--avalon8-freq-sel <arg> Set Avalon8 default frequency select, range:[0, 3], step: 1, example: 3 (default: 3)
+--avalon8-fan       Set Avalon8 target fan speed, range:[0, 100], step: 1, example: 0-100
+--avalon8-temp <arg> Set Avalon8 target temperature, range:[0, 100] (default: 90)
+--avalon8-polling-delay <arg> Set Avalon8 polling delay value (ms) (default: 20)
+--avalon8-aucspeed <arg> Set AUC3 IIC bus speed (default: 400000)
+--avalon8-aucxdelay <arg> Set AUC3 IIC xfer read delay, 4800 ~= 1ms (default: 19200)
+--avalon8-smart-speed <arg> Set Avalon8 smart speed, range 0-1. 0 means Disable (default: 1)
+--avalon8-th-pass <arg> Set A3210 th pass value (default: -1)
+--avalon8-th-fail <arg> Set A3210 th fail value (default: -1)
+--avalon8-th-init <arg> Set A3210 th init value (default: 32767)
+--avalon8-th-ms <arg> Set A3210 th ms value (default: 5)
+--avalon8-th-timeout <arg> Set A3210 th timeout value (default: 4294967295)
+--avalon8-th-add <arg> Set A3210 th add value (default: 1)
+--avalon8-iic-detect Enable Avalon8 detect through iic controller
+--avalon8-nonce-mask <arg> Set A3210 nonce mask, range 24-32. (default: -1)
+--avalon8-nonce-check <arg> Set A3210 nonce check, range 0-1. (default: 1)
+--avalon8-roll-enable <arg> Set A3210 roll enable, range 0-1. (default: 1)
+--avalon8-mux-l2h <arg> Set Avalon8 mux l2h, range 0-2. (default: 0)
+--avalon8-mux-h2l <arg> Set Avalon8 mux h2l, range 0-1. (default: 1)
+--avalon8-h2ltime0-spd <arg> Set Avalon8 h2ltime0 spd, range 0-255. (default: 3)
+--avalon8-spdlow <arg> Set Avalon8 spdlow, range 0-3. (default: -1)
+--avalon8-spdhigh <arg> Set Avalon8 spdhigh, range 0-3. (default: 3)
+--avalon8-cinfo-asic Set Avalon8 cinfo asic index, range:[0, 25], step: 1
+--avalon8-pid-p <arg> Set Avalon8 pid-p, range 0-9999. (default: 2)
+--avalon8-pid-i <arg> Set Avalon8 pid-i, range 0-9999. (default: 5)
+--avalon8-pid-d <arg> Set Avalon8 pid-d, range 0-9999. (default: 0)
+
+BFLSC Devices
+
+--bflsc-overheat <arg> Set overheat temperature where BFLSC devices throttle, 0 to disable (default: 90)
+
+This will allow you to change or disable the default temperature where cgminer
+throttles BFLSC devices by allowing them to temporarily go idle.
+
+
+BITFURY Devices
+
+--bxf-bits <arg>    Set max BXF/HXF bits for overclocking (default: 54)
+
+In combination with the dynamic clocking on Bi*fury devices, this sets the
+highest bit target that cgminer will aim for.
+
+
+--bxf-temp-target <arg> Set target temperature for BXF/HXF devices (default: 82)
+
+Cgminer uses dynamic clocking on Bi*fury devices to try and maintain the
+temperature just below an optimal target. This option allows you to change the
+target temperature. When actively cooled below this, the devices will run at
+maximum speed.
+
+--bxm-bits <arg>    Set BXM bits for overclocking (default: 54)
+
+Choose the overclocking  bits for BFx2 devices.
+
+
+--nfu-bits <arg>    Set nanofury bits for overclocking range 32-63 (default: 50)
+
+Cgminer by default sets the clockspeed on nanofury devices to the highest that
+is still within USB2 spec. This value allows you to alter the clockspeed, with
+~54 being the optimal but requiring a higher power or USB3 port.
+
+
+Cointerra Devices
+
+--cta-load <arg> (0 - 255)
+--ps-load <arg> (0 - 100)
+
+These are undocumented.
+
+
+Drillbit Systems Devices
+
+--drillbit-options <arg> Set drillbit options <int|ext>:clock[:clock_divider][:voltage]
+
+* int/ext defines the clock source - default int. Not all boards support ext.
+* clock_divider must be 1 or 2 with a default of 1. Bitfury only,
+  ignored on Avalon.
+* clock is in MHz, on Drillbit range 80-250 with a default of 200,
+  recommended maximum 230. On Avalon range 500-1000 with a
+  recommended maximum of 800.
+* voltage is ASIC core voltage in millivolts, available values vary per board but
+  default is 850 and the recommended maximum is 950 (Bitfury) and 1000 (Avalon.)
+
+--drillbit-auto <every>:[<gooderr>:<baderr>:<maxerr>]
+
+If supported by firmware and device, this feature allows cgminer to
+automatically tweak each ASIC's clock rate up and down in to achieve
+optimal performance.
+
+* every - only required param, check each ASIC after each block of
+  this many work units. Recommended value 100.
+* gooderr - the "Good" threshold is when less hardware errors than
+  this per "every" work units, the clock rate will be increased.
+  Default value 1.
+* baderr - the "Bad" threshold is when more hardware errors than
+  this per "every" work units, the clock rate will be decreased.
+  Default value 3.
+* maxerr - the "Max" threshold is when more hardware errors than
+  this per "every" work units (including pre-empting before
+  "every" work units is up), the clock rate will be decreased and
+  will not be increased again past this point. Default value 10.
+
+
+BlackArrow Bitfury devices
+
+--bab-options <arg> Set BaB options Max:Def:Min:Up:Down:Hz:Delay:Trf
+
+Any option left blank or starting with 'd' will use the default setting
+If there are not enough options, then the remaining will be left at their
+default value
+
+Max:Def:Min are the chip speed limits to allow, ranging from 52 to 57
+
+Up:Down are the HW error % used to tune the chip speed
+Up means if the HW error % is less than up, over a 5 minute period,
+then increase the chip speed
+Down means if the HW error % is greater than down, over 5 minutes,
+then decrease the chip speed
+
+Hz is the SPI clock speed to use
+
+Delay is the us delay used between bytes for the SPI I/O - default 0
+
+Trf is the us delay used between sends for the SPI I/O - default 0
+
+
+Hashfast devices
+
+--hfa-hash-clock <arg> Set hashfast clock speed (default: 550)
+
+This will change the initialisation clock speed on all attached hfa devices.
+Note that if instability is detected by cgminer and the device has to undergo
+a reset, cgminer will lower the clockspeed on resetting it each time till the
+value returns to the default of 550.
+
+--hfa-fail-drop <arg> Set how many MHz to drop clockspeed each failure on an overlocked hashfast device (default: 10)
+
+If you overclock your hashfast device with --hfa-hash-clock and cgminer detects
+it failing to return hashes, it will restart it at a lower clock speed if
+possible. Changing this value will allow you to choose how much it will lower
+the clock speed or to disable this function entirely.
+
+--hfa-fan <arg>     Set fanspeed percentage for hashfast, single value or range (default: 10-85)
+
+This changes the range of fanspeeds used on hashfast devices with firmware that
+supports it. Note that the fanspeed will dynamically change to try and maintain
+a target temperature with --hfa-temp-target but if the target temperature is
+disabled, the fanspeed will remain static.
+eg:
+--hfa-fan 25-100
+
+--hfa-temp-overheat <arg> Set the hashfast overheat throttling temperature (default: 95)
+
+Cgminer will temporarily stop sending hashfast devices work once this
+temperature is reached. Note that with the water cooling in these devices,
+temperature recovery is likely to be very quick and the device will start
+hashing again after only a very brief period.
+
+--hfa-temp-target <arg> Set the hashfast target temperature (0 to disable) (default: 88)
+
+On hashfast devices with firmware that supports dynamic fanspeed and die speeds,
+cgminer will try to maintain temperature according to this target by adjusting
+fanspeed and then if need be, throttle speeds on a die-by-die basis. Disabling
+this feature will leave a constant fanspeed and die speed but will not disable
+the temp-overheat feature.
+
+--hfa-name <arg>    Set a unique name for a single hashfast device specified with --usb or the first device found
+
+This command allows you to specify the unique name stored in nvram on a single
+hashfast device. This name can be queried from the API stats command and comes
+up as "op name". Discrete names are used by cgminer to try to maintain settings
+across restarts, unplugs/hotplugs and so on. If this command is used by itself,
+the name will be given to the first hashfast device it encounters and then
+cgminer will proceed to go back to regular mining. If you have multiple devices,
+it is best to discretely choose the device you wish to use with the --usb
+command. For example
+'lsusb' on linux shows the following devices (297c:0001 is a hfa device):
+ Bus 001 Device 079: ID 297c:0001
+ Bus 004 Device 042: ID 297c:0001
+If you wished to name the second device Slug you would add the commands:
+ --hfa-name Slug --usb 4:42
+
+--hfa-noshed        Disable hashfast dynamic core disabling feature
+
+Newer firmwares on hashfast devices dynamically disable cores that generate
+invalid data. This command will disable this feature where possible.
+
+--hfa-options <arg> Set hashfast options name:clock or clock@voltage (comma separated)
+
+This command allows you to set options for each discrete hashfast device by
+its name (if the firmware has naming support, i.e. version 0.3+). Currently
+this takes as option the clock speed alone or clock speed and voltage,
+although future options may be added.
+e.g.:
+--hfa-options "rabbit:650,turtle:550@800"
+
+Would set a device named rabbit to clock speed 650 MHz using default voltage
+and the one named turtle to 550 MHz using a voltage of 800 mv. Starting the
+device at a speed where it is most stable will give more reliable hashrates
+long term and prevent it interacting with other devices, rather than depending
+on the clockdown feature in cgminer.
+
+Note: Setting voltage cause a board reset and hotplug event on cgminer startup.
+
+Other undocumented hashfast command line options are for development purposes
+only at this stage and serve no useful purpose to end users.
+
+
+Hashratio Devices
+
+--hro-freq          Set the hashratio clock frequency (default: 280)
+
+
+Bitmine A1 Devices
+
+--bitmine-a1-options <ref_clk>:<sys_clk>:<spi_clk>:<max_chip>
+ref_clk:  reference clock in kHz                      (default: 16000)
+sys_clk:  target system clock in kHz to be set in PLL (default: 250000)
+spi_clk:  SPI clock in kHz                            (default: 800)
+max_chip: [debug/testing] limit chip chain
+
+Set 0 for fields you want to keep untouched to default, e.g.
+--bitmine-a1-options 0:0:400
+to only set SPI clock to 400kHz
+
+
+Rockminer R-Box Devices
+
+--rock-freq <arg>   Set RockMiner frequency in MHz, range 125-500 (default: 270)
+
+Note that only a limited range is likely to be accepted (usually 200-290)
+

+ 8 - 0
AUTHORS

@@ -0,0 +1,8 @@
+Current maintainers and active developers:
+API+USB+FPGA+ASIC: Andrew Smith <kan0i {at} kano-kun [dot] net> 1Jjk2LmktEQKnv8r2cZ9MvLiZwZ9gxabKm
+Gekko: https://github.com/vthoang
+
+Legacy:
+Original CPU mining software: Jeff Garzik <jgarzik@pobox.com>
+BitFORCE FPGA mining and refactor: Luke Dashjr <luke-jr+cgminer@utopios.org> 1NbRmS6a4dniwHHoSS9v3tEYUpP1Z5VVdL
+Various code: Con Kolivas <kernel@kolivas.org> 15qSxP1SQcUX3o4nhkfdbgyoWEFMomJ4rZ

+ 674 - 0
COPYING

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

+ 6 - 0
ChangeLog

@@ -0,0 +1,6 @@
+See git repository ('git log') for full changelog.
+
+Git repo can be found at:
+https://github.com/kanoi/cgminer
+
+The NEWS file contains most of the changelog

+ 224 - 0
FPGA-README

@@ -0,0 +1,224 @@
+
+This README contains extended details about FPGA mining with cgminer
+
+
+For ModMinerQuad (MMQ) BitForce (BFL) and Icarus (ICA, BLT, LLT, AMU, CMR)
+--------------------------------------------------------------------------
+
+See README 'SETTING UP USB DEVICES' for setting up USB mining with cgminer
+
+
+ModMinerQuad (MMQ)
+------------------
+
+The mining bitstream does not survive a power cycle, so cgminer will upload
+it, if it needs to, before it starts mining (approx 7min 40sec)
+
+The red LED also flashes while it is uploading the bitstream
+
+-
+
+If the MMQ doesn't respond to cgminer at all, or the red LED isn't flashing
+then you will need to reset the MMQ
+
+The red LED should always be flashing when it is mining or ready to mine
+
+To reset the MMQ, you are best to press the left "RESET" button on the
+backplane, then unplug and replug the USB cable
+
+If your MMQ doesn't have a button on the "RESET" pad, you need to join
+the two left pads of the "RESET" pad with conductive wire to reset it.
+Cutting a small (metal) paper-clip in half works well for this
+
+Then unplug the USB cable, wait for 5 seconds, then plug it back in
+
+After you press reset, the red LED near the USB port should blink continuously
+
+If it still wont work, power off, wait for 5 seconds, then power on the MMQ
+This of course means it will upload the bitstream again when you start cgminer
+
+-
+
+Device 0 is on the power end of the board
+
+-
+
+You must make sure you have an approriate firmware in your MMQ
+Read here for official details of changing the firmware:
+ http://wiki.btcfpga.com/index.php?title=Firmware
+
+The basics of changing the firmware are:
+ You need two short pieces of conductive wire if your MMQ doesn't have
+ buttons on the "RESET" and "ISP" pads on the backplane board
+ Cutting a small (metal) paper-clip in half works well for this
+
+ Join the 2 left pads of the "RESET" pad with wire and the led will dim
+ Without disconnecting the "RESET", join the 2 left pads of the "ISP" pad
+ with a wire and it will stay dim
+ Release "RESET" then release "ISP" and is should still be dim
+ Unplug the USB and when you plug it back in it will show up as a mass
+ storage device
+  Linux: (as one single line):
+   mcopy -i /dev/disk/by-id/usb-NXP_LPC134X_IFLASH_ISP000000000-0:0
+      modminer091012.bin ::/firmware.bin
+  Windows: delete the MSD device file firmware.bin and copy in the new one
+   rename the new file and put it under the same name 'firmware.bin'
+ Disconnect the USB correctly (so writes are flushed first)
+ Join and then disconnect "RESET" and then plug the USB back in and it's done
+
+Best to update to one of the latest 2 listed below if you don't already
+have one of them in your MMQ
+
+The current latest different firmware are:
+
+ Latest for support of normal or TLM bitstream:
+  http://btcfpga.com/files/firmware/modminer092612-TLM.bin
+
+ Latest with only normal bitstream support (Temps/HW Fix):
+  http://btcfpga.com/files/firmware/modminer091012.bin
+
+The code is currently tested on the modminer091012.bin firmware.
+This comment will be updated when others have been tested
+
+-
+
+On many linux distributions there is an app called modem-manager that
+may cause problems when it is enabled, due to opening the MMQ device
+and writing to it
+
+The problem will typically present itself by the flashing led on the
+backplane going out (no longer flashing) and it takes a power cycle to
+re-enable the MMQ firmware - which then can lead to the problem happening
+again
+
+You can either disable/uninstall modem-manager if you don't need it or:
+a (hack) solution to this is to blacklist the MMQ USB device in
+/lib/udev/rules.d/77-mm-usb-device-blacklist.rules
+
+Adding 2 lines like this (just above APC) should help
+# MMQ
+ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0003", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+The change will be lost and need to be re-done, next time you update the
+modem-manager software
+
+TODO: check that all MMQ's have the same product ID
+
+
+BitForce (BFL)
+--------------
+
+--bfl-range         Use nonce range on bitforce devices if supported
+
+This option is only for bitforce devices. Earlier devices such as the single
+did not have any way of doing small amounts of work which meant that a lot of
+work could be lost across block changes. Some of the "minirigs" have support
+for doing this, so less work is lost across a longpoll. However, it comes at
+a cost of 1% in overall hashrate so this feature is disabled by default. It
+is only recommended you enable this if you are mining with a minirig on
+p2pool.
+
+C source is included for a bitforce firmware flash utility on Linux only:
+ bitforce-firmware-flash.c
+Using this, you can change the bitstream firmware on bitforce singles.
+It is untested with other devices. Use at your own risk!
+
+To compile:
+ make bitforce-firmware-flash
+To flash your BFL, specify the BFL port and the flash file e.g.:
+ sudo ./bitforce-firmware-flash /dev/ttyUSB0 alphaminer_832.bfl
+It takes a bit under 3 minutes to flash a BFL and shows a progress % counter
+Once it completes, you may also need to wait about 15 seconds,
+then power the BFL off and on again
+
+If you get an error at the end of the BFL flash process stating:
+ "Error reading response from ZBX"
+it may have worked successfully anyway.
+Test mining on it to be sure if it worked or not.
+
+You need to give cgminer about 10 minutes mining with the BFL to be sure of
+the MH/s value reported with the changed firmware - and the MH/s reported
+will be less than the firmware speed since you lose work on every block change.
+
+
+Icarus (ICA, BLT, LLT, AMU, CMR)
+--------------------------------
+
+There are two hidden options in cgminer when Icarus support is compiled in:
+
+--icarus-options <arg> Set specific FPGA board configurations - one set of values for all or comma separated
+           baud:work_division:fpga_count
+
+           baud           The Serial/USB baud rate - 115200 or 57600 only - default 115200
+           work_division  The fraction of work divided up for each FPGA chip - 1, 2, 4 or 8
+                          e.g. 2 means each FPGA does half the nonce range - default 2
+           fpga_count     The actual number of FPGA working - this would normally be the same
+                          as work_division - range is from 1 up to 'work_division'
+                          It defaults to the value of work_division - or 2 if you don't specify
+                          work_division
+
+If you define fewer comma seperated values than Icarus devices, the last values will be used
+for all extra devices
+
+An example would be: --icarus-options 57600:2:1
+This would mean: use 57600 baud, the FPGA board divides the work in half however
+only 1 FPGA actually runs on the board (e.g. like an early CM1 Icarus copy bitstream)
+
+--icarus-timing <arg> Set how the Icarus timing is calculated - one setting/value for all or comma separated
+           default[=N]   Use the default Icarus hash time (2.6316ns)
+           short=[N]     Calculate the hash time and stop adjusting it at ~315 difficulty 1 shares (~1hr)
+           long=[N]      Re-calculate the hash time continuously
+           value[=N]     Specify the hash time in nanoseconds (e.g. 2.6316) and abort time (e.g. 2.6316=80)
+
+If you define fewer comma seperated values than Icarus devices, the last values will be used
+for all extra devices
+
+Icarus timing is required for devices that do not exactly match a default Icarus Rev3 in
+processing speed
+If you have an Icarus Rev3 you should not normally need to use --icarus-timing since the
+default values will maximise the MH/s and display it correctly
+
+Icarus timing is used to determine the number of hashes that have been checked when it aborts
+a nonce range (including on a LongPoll)
+It is also used to determine the elapsed time when it should abort a nonce range to avoid
+letting the Icarus go idle, but also to safely maximise that time
+
+'short' or 'long' mode should only be used on a computer that has enough CPU available to run
+cgminer without any CPU delays (an active desktop or swapping computer would not be stable enough)
+Any CPU delays while calculating the hash time will affect the result
+'short' mode only requires the computer to be stable until it has completed ~315 difficulty 1 shares
+'long' mode requires it to always be stable to ensure accuracy, however, over time it continually
+corrects itself
+The optional additional =N for 'short' or 'long' specifies the limit to set the timeout to in N * 100ms
+thus if the timing code calculation is higher while running, it will instead use N * 100ms
+This can be set to the appropriate value to ensure the device never goes idle even if the
+calculation is negatively affected by system performance
+
+When in 'short' or 'long' mode, it will report the hash time value each time it is re-calculated
+In 'short' or 'long' mode, the scan abort time starts at 5 seconds and uses the default 2.6316ns
+scan hash time, for the first 5 nonce's or one minute (whichever is longer)
+
+In 'default' or 'value' mode the 'constants' are calculated once at the start, based on the default
+value or the value specified
+The optional additional =N specifies to set the default abort at N * 100ms, not the calculated
+value, which is ~112 for 2.6316ns
+
+To determine the hash time value for a non Icarus Rev3 device or an Icarus Rev3 with a different
+bitstream to the default one, use 'long' mode and give it at least a few hundred shares, or use
+'short' mode and take note of the final hash time value (Hs) calculated
+You can also use the RPC API 'stats' command to see the current hash time (Hs) at any time
+
+The Icarus code currently only works with an FPGA device that supports the same commands as
+Icarus Rev3 requires and also is less than ~840MH/s and greater than 2MH/s
+If an FPGA device does hash faster than ~840MH/s it should work correctly if you supply the
+correct hash time nanoseconds value
+
+The Icarus code will automatically detect Icarus, Lancelot, AsicminerUSB and Cairnsmore1
+FPGA devices and set default settings to match those devices if you don't specify them
+
+The timing code itself will affect the Icarus performance since it increases the delay after
+work is completed or aborted until it starts again
+The increase is, however, extremely small and the actual increase is reported with the
+RPC API 'stats' command (a very slow CPU will make it more noticeable)
+Using the 'short' mode will remove this delay after 'short' mode completes
+The delay doesn't affect the calculation of the correct hash time

+ 3 - 0
LICENSE

@@ -0,0 +1,3 @@
+Cgminer is available under the terms of the GNU Public License version 3.
+
+See COPYING for details.

BIN
MCast.class


+ 333 - 0
MCast.java

@@ -0,0 +1,333 @@
+/*
+ *
+ * Copyright (C) Andrew Smith 2013
+ *
+ * Usage: java MCast [-v] code toaddr port replyport wait
+ *
+ * If any are missing or blank they use the defaults:
+ *
+ *	-v means report how long the last reply took
+ *
+ *	code		= 'FTW'
+ *	toaddr		= '224.0.0.75'
+ *	port		= '4028'
+ *	replyport	= '4027'
+ *	wait		= '1000'
+ *
+ */
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+
+class MCast implements Runnable
+{
+	static private final String MCAST_CODE = "FTW";
+	static private final String MCAST_ADDR = "224.0.0.75";
+	static private final int MCAST_PORT = 4028;
+	static private final int MCAST_REPORT = 4027;
+	static private final int MCAST_WAIT4 = 1000;
+
+	static private String code = MCAST_CODE;
+	static private String addr = MCAST_ADDR;
+	static private int port = MCAST_PORT;
+	static private int report = MCAST_REPORT;
+	static private int wait4 = MCAST_WAIT4;
+
+	private InetAddress mcast_addr = null;
+
+	static private final Integer lock = new Integer(666);
+
+	static private boolean ready = false;
+
+	static private Thread listen = null;
+
+	static public boolean verbose = false;
+
+	static private Date start = null;
+	static private Date last = null;
+	static boolean got_last = false;
+
+	static public void usAge()
+	{
+		System.err.println("usAge: java MCast [-v] [code [toaddr [port [replyport [wait]]]]]");
+		System.err.println("       -v=report elapsed ms to last reply");
+		System.err.println("     Anything below missing or blank will use it's default");
+		System.err.println("       code=X in cgminer-X-Port default="+MCAST_CODE);
+		System.err.println("       toaddr=multicast address default="+MCAST_ADDR);
+		System.err.println("       port=multicast port default="+MCAST_PORT);
+		System.err.println("       replyport=local post to listen for replies default="+MCAST_REPORT);
+		System.err.println("       wait=how long to wait for replies default="+MCAST_WAIT4+"ms");
+		System.exit(1);
+	}
+
+	private int port(String _port, String name)
+	{
+		int tmp = 0;
+
+		try
+		{
+			tmp = Integer.parseInt(_port);
+		}
+		catch (NumberFormatException nfe)
+		{
+			System.err.println("Invalid " + name + " - must be a number between 1 and 65535");
+			usAge();
+			System.exit(1);
+		}
+
+		if (tmp < 1 || tmp > 65535)
+		{
+			System.err.println("Invalid " + name + " - must be a number between 1 and 65535");
+			usAge();
+			System.exit(1);
+		}
+
+		return tmp;
+	}
+
+	public void set_code(String _code)
+	{
+		if (_code.length() > 0)
+			code = _code;
+	}
+
+	public void set_addr(String _addr)
+	{
+		if (_addr.length() > 0)
+		{
+			addr = _addr;
+
+			try
+			{
+				mcast_addr = InetAddress.getByName(addr);
+			}
+			catch (Exception e)
+			{
+				System.err.println("ERR: Invalid multicast address");
+				usAge();
+				System.exit(1);
+			}
+		}
+	}
+
+	public void set_port(String _port)
+	{
+		if (_port.length() > 0)
+			port = port(_port, "port");
+	}
+
+	public void set_report(String _report)
+	{
+		if (_report.length() > 0)
+			report = port(_report, "reply port");
+	}
+
+	public void set_wait(String _wait4)
+	{
+		if (_wait4.length() > 0)
+		{
+			try
+			{
+				wait4 = Integer.parseInt(_wait4);
+			}
+			catch (NumberFormatException nfe)
+			{
+				System.err.println("Invalid wait - must be a number between 0ms and 60000ms");
+				usAge();
+				System.exit(1);
+			}
+
+			if (wait4 < 0 || wait4 > 60000)
+			{
+				System.err.println("Invalid wait - must be a number between 0ms and 60000ms");
+				usAge();
+				System.exit(1);
+			}
+		}
+	}
+
+	public void run() // listen
+	{
+		byte[] message = new byte[1024];
+		DatagramSocket socket = null;
+		DatagramPacket packet = null;
+
+		try
+		{
+			socket = new DatagramSocket(report);
+			packet = new DatagramPacket(message, message.length);
+
+			synchronized (lock)
+			{
+				ready = true;
+			}
+
+			while (true)
+			{
+				socket.receive(packet);
+
+				synchronized (lock)
+				{
+					last = new Date();
+				}
+
+				int off = packet.getOffset();
+				int len = packet.getLength();
+
+				System.out.println("Got: '" + new String(message, off, len) + "' from" + packet.getSocketAddress());
+			}
+		}
+		catch (Exception e)
+		{
+			socket.close();
+		}
+	}
+
+	public void sendMCast()
+	{
+		try
+		{
+			String message = new String("cgminer-" + code + "-" + report);
+			MulticastSocket socket = null;
+			DatagramPacket packet = null;
+
+			socket = new MulticastSocket();
+			packet = new DatagramPacket(message.getBytes(), message.length(), mcast_addr, port);
+
+			System.out.println("About to send " + message + " to " + mcast_addr + ":" + port);
+
+			start = new Date();
+
+			socket.send(packet);
+
+			socket.close();
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+		}
+	}
+
+	public void init()
+	{
+		MCast lis = new MCast();
+		listen = new Thread(lis);
+		listen.start();
+
+		while (true)
+		{
+			synchronized (lock)
+			{
+				if (ready)
+					break;
+			}
+
+			try
+			{
+				Thread.sleep(100);
+			}
+			catch (Exception sl1)
+			{
+			}
+		}
+
+		try
+		{
+			Thread.sleep(500);
+		}
+		catch (Exception sl2)
+		{
+		}
+
+		sendMCast();
+
+		try
+		{
+			Thread.sleep(wait4);
+		}
+		catch (Exception sl3)
+		{
+		}
+
+		listen.interrupt();
+
+		if (verbose)
+		{
+			try
+			{
+				Thread.sleep(100);
+			}
+			catch (Exception sl4)
+			{
+			}
+
+			synchronized (lock)
+			{
+				if (last == null)
+					System.out.println("No replies received");
+				else
+				{
+					long diff = last.getTime() - start.getTime();
+					System.out.println("Last reply took " + diff + "ms");
+				}
+			}
+		}
+
+		System.exit(0);
+	}
+
+	public MCast()
+	{
+	}
+
+	public static void main(String[] params) throws Exception
+	{
+		int p = 0;
+
+		MCast mcast = new MCast();
+
+		mcast.set_addr(MCAST_ADDR);
+
+		if (params.length > p)
+		{
+			if (params[p].equals("-?")
+			||  params[p].equalsIgnoreCase("-h")
+			||  params[p].equalsIgnoreCase("-help")
+			||  params[p].equalsIgnoreCase("--help"))
+				MCast.usAge();
+			else
+			{
+				if (params[p].equals("-v"))
+				{
+					mcast.verbose = true;
+					p++;
+				}
+
+				if (params.length > p)
+				{
+					mcast.set_code(params[p++]);
+
+					if (params.length > p)
+					{
+						mcast.set_addr(params[p++]);
+
+						if (params.length > p)
+						{
+							mcast.set_port(params[p++]);
+
+							if (params.length > p)
+							{
+								mcast.set_report(params[p++]);
+								if (params.length > p)
+									mcast.set_wait(params[p]);
+							}
+						}
+					}
+				}
+			}
+		}
+
+		mcast.init();
+	}
+}

+ 233 - 0
Makefile.am

@@ -0,0 +1,233 @@
+
+ACLOCAL_AMFLAGS = -I m4
+
+JANSSON_CPPFLAGS= -I$(top_builddir)/compat/jansson-2.9/src -I$(top_srcdir)/compat/jansson-2.9/src
+
+if WANT_USBUTILS
+USBUTILS_CPPFLAGS = -I$(top_builddir)/compat/libusb-1.0/libusb
+else
+USBUTILS_CPPFLAGS =
+endif
+
+if STATIC_CURL
+LIBCURL_CPPFLAGS = -DCURL_STATICLIB
+else
+LIBCURL_CPPFLAGS =
+endif
+
+EXTRA_DIST	= example.conf linux-usb-cgminer \
+		  api-example.php miner.php	\
+		  API.class API.java api-example.c windows-build.txt \
+		  bitstreams/README API-README FPGA-README \
+		  bitforce-firmware-flash.c hexdump.c ASIC-README \
+		  01-cgminer.rules
+
+SUBDIRS		= lib compat ccan
+
+cgminer_CPPFLAGS = $(PTHREAD_FLAGS) -fcommon -fno-strict-aliasing $(JANSSON_CPPFLAGS) $(USBUTILS_CPPFLAGS) $(LIBCURL_CPPFLAGS)
+
+bin_PROGRAMS	= cgminer
+
+cgminer_LDFLAGS	= $(PTHREAD_FLAGS)
+cgminer_LDADD	= $(DLOPEN_FLAGS) @LIBCURL_LIBS@ @JANSSON_LIBS@ @LIBZ_LIBS@ @PTHREAD_LIBS@ \
+		  @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \
+		  @LIBUSB_LIBS@ @MM_LIBS@ @RT_LIBS@ @LIBSYSTEMD_LIBS@ \
+		  @MATH_LIBS@ lib/libgnu.a ccan/libccan.a
+
+cgminer_CPPFLAGS += -I$(top_builddir)/lib -I$(top_srcdir)/lib
+
+if !HAVE_WINDOWS
+cgminer_CPPFLAGS += @LIBCURL_CFLAGS@
+endif
+
+# common sources
+cgminer_SOURCES := cgminer.c
+
+cgminer_SOURCES	+= elist.h miner.h compat.h bench_block.h	\
+		   util.c util.h uthash.h logging.h		\
+		   sha2.c sha2.h api.c
+
+cgminer_SOURCES	+= logging.c
+
+cgminer_SOURCES	+= klist.h klist.c
+
+cgminer_SOURCES	+= noncedup.c
+
+if NEED_FPGAUTILS
+cgminer_SOURCES += fpgautils.c fpgautils.h
+endif
+
+if WANT_USBUTILS
+cgminer_SOURCES += usbutils.c usbutils.h
+endif
+
+if WANT_LIBBITFURY
+cgminer_SOURCES += libbitfury.c libbitfury.h mcp2210.c mcp2210.h
+endif
+
+if WANT_CRC16
+cgminer_SOURCES += crc16.c crc.h
+endif
+
+# Device drivers
+if HAS_AVALON
+cgminer_SOURCES += driver-avalon.c driver-avalon.h
+endif
+
+if HAS_KNC
+cgminer_SOURCES += driver-knc.c knc-asic.c knc-asic.h knc-transport.h knc-transport-spi.c
+cgminer_LDADD   += -lz
+endif
+
+if HAS_BFLSC
+cgminer_SOURCES += driver-bflsc.c driver-bflsc.h
+endif
+
+if HAS_BITFORCE
+cgminer_SOURCES += driver-bitforce.c
+endif
+
+if HAS_HASHFAST
+cgminer_SOURCES += driver-hashfast.c driver-hashfast.h hf_protocol.h hf_protocol_be.h
+endif
+
+if HAS_HASHRATIO
+cgminer_SOURCES += driver-hashratio.c driver-hashratio.h
+endif
+
+if HAS_BITFURY
+cgminer_SOURCES += driver-bitfury.c driver-bitfury.h
+endif
+
+if HAS_BITFURY16
+cgminer_SOURCES += bf16-bitfury16.c bf16-bitfury16.h
+cgminer_SOURCES += bf16-brd-control.c bf16-brd-control.h
+cgminer_SOURCES += bf16-communication.c bf16-communication.h
+cgminer_SOURCES += bf16-ctrldevice.c bf16-ctrldevice.h
+cgminer_SOURCES += bf16-device.h
+cgminer_SOURCES += bf16-gpiodevice.c bf16-gpiodevice.h
+cgminer_SOURCES += bf16-mspcontrol.c bf16-mspcontrol.h
+cgminer_SOURCES += bf16-spidevice.c bf16-spidevice.h
+cgminer_SOURCES += bf16-uartdevice.c bf16-uartdevice.h
+cgminer_SOURCES += driver-bitfury16.c driver-bitfury16.h
+endif
+
+if HAS_BITMAIN_SOC
+cgminer_SOURCES += driver-btm-soc.c driver-btm-soc.h
+cgminer_SOURCES += bitmain-board-test.c bitmain-board-test.h
+endif
+
+if HAS_BITMINE_A1
+cgminer_SOURCES += driver-SPI-bitmine-A1.c
+cgminer_SOURCES += spi-context.c spi-context.h
+cgminer_SOURCES += A1-common.h
+cgminer_SOURCES += A1-board-selector.h
+cgminer_SOURCES += A1-board-selector-CCD.c A1-board-selector-CCR.c
+cgminer_SOURCES += A1-trimpot-mcp4x.h A1-trimpot-mcp4x.c
+cgminer_SOURCES += i2c-context.c i2c-context.h
+endif
+
+if HAS_DRAGONMINT_T1
+cgminer_SOURCES += driver-SPI-dragonmint-t1.c
+cgminer_SOURCES += dragonmint_t1.c
+cgminer_SOURCES += dm_temp_ctrl.c
+cgminer_SOURCES += dm_fan_ctrl.c
+cgminer_SOURCES += dm_compat.c
+endif
+
+if HAS_DRILLBIT
+cgminer_SOURCES += driver-drillbit.c driver-drillbit.h
+endif
+
+if HAS_ICARUS
+cgminer_SOURCES += driver-icarus.c
+endif
+
+if HAS_KLONDIKE
+cgminer_SOURCES += driver-klondike.c
+endif
+
+if HAS_COINTERRA
+cgminer_SOURCES += driver-cointerra.c driver-cointerra.h
+endif
+
+if HAS_SP10
+cgminer_SOURCES += driver-spondoolies-sp10.c driver-spondoolies-sp10.h \
+		   driver-spondoolies-sp10-p.c driver-spondoolies-sp10-p.h
+endif
+
+
+if HAS_SP30
+cgminer_SOURCES += driver-spondoolies-sp30.c driver-spondoolies-sp30.h \
+		   driver-spondoolies-sp30-p.c driver-spondoolies-sp30-p.h
+endif
+
+if HAS_BAB
+cgminer_SOURCES += driver-bab.c
+endif
+
+if HAS_AVALON2
+cgminer_SOURCES += driver-avalon2.c driver-avalon2.h
+endif
+
+if HAS_AVALON4
+cgminer_SOURCES += driver-avalon4.c driver-avalon4.h
+endif
+
+if HAS_AVALON7
+cgminer_SOURCES += driver-avalon7.c driver-avalon7.h
+endif
+
+if HAS_AVALON8
+cgminer_SOURCES += driver-avalon8.c driver-avalon8.h
+endif
+
+if HAS_AVALON9
+cgminer_SOURCES += driver-avalon9.c driver-avalon9.h
+endif
+
+if HAS_AVALONLC3
+cgminer_SOURCES += driver-avalonlc3.c driver-avalonlc3.h
+
+endif
+if NEED_I2C_CONTEXT
+cgminer_SOURCES += i2c-context.c
+endif
+
+if SUPPORT_SSP
+cgminer_SOURCES += libssplus.c
+endif
+
+if HAS_AVALON_MINER
+cgminer_SOURCES += driver-avalon-miner.c driver-avalon-miner.h
+endif
+
+if HAS_MINION
+cgminer_SOURCES += driver-minion.c
+endif
+
+if HAS_ANT_S1
+cgminer_SOURCES += driver-bitmain.c driver-bitmain.h
+endif
+
+if HAS_ANT_S2
+cgminer_SOURCES += driver-bitmain.c driver-bitmain.h
+endif
+
+if HAS_ANT_S3
+cgminer_SOURCES += driver-bitmain.c driver-bitmain.h
+endif
+
+if HAS_MODMINER
+cgminer_SOURCES += driver-modminer.c
+bitstreamsdir = $(bindir)/bitstreams
+dist_bitstreams_DATA = $(top_srcdir)/bitstreams/README
+endif
+
+if HAS_BLOCKERUPTER
+cgminer_SOURCES += driver-blockerupter.c driver-blockerupter.h
+endif
+
+if HAS_GEKKO
+cgminer_SOURCES += driver-gekko.c driver-gekko.h
+endif

+ 7412 - 0
NEWS

@@ -0,0 +1,7412 @@
+Version 4.12.0 - 16th September 2021
+
+- Handle gcc 7+ issues/warnings in main and gekko code
+- Pull in all Gekko code from https://github.com/vthoang/cgminer
+- Pull in all available avalon updates, Avalon8
+- Update miner.php v42.14
+- Update information for latest git source
+- Add Gekko linux support for BM1397 (GSF - compacf)
+- General Gekko code updates
+- New Windows 10 build instructions
+- Gekko now tested on Windows 10 also
+
+Version 4.11.1 - 16th August 2018
+
+- Alias T1factory option to noauto.
+- sem_timedwait needs to use CLOCK_REALTIME.
+- Conditional timedwaits need to use CLOCK_REALTIME.
+- Remove unused abstime.
+
+
+Version 4.11.0 - 15th August 2018
+
+- Disable implicit fallthrough warning for newer GCCs.
+- Alphabetical options.
+- Cope with abs timeouts not working on some OSs, fixing a logic error on
+cgsem_mswait.
+- hash table: use lookup instead of iteration
+- Do away with useless factory tune.
+- Keep diff low for accurate hashrates.
+- Take an optional fanspeed target allowing for quiet operation.
+- Change default pll back to 1332.
+- Set lastshare on successful reinit and sleep longer for each retry.
+- Allow much tighter temperature control if temperature is unchanged for a
+longer period, allowing temps to get to 75 degrees and have optimally low
+fanspeed.
+- Use clock_gettime instead of gettimeofday to not be burnt by ntp changes.
+- Remove useless variables.
+- Should be 390k.
+- Set lower spi speed to allow ultra low frequencies on shutdown.
+- Re-initialise chains first if possible instead of shutting down cgminer.
+- Abstract out prepare T1.
+- Abstract out start t1 chain.
+- More reliably shut down cgminer from the driver thread.
+- Import SPI fixes.
+- Import dev fixes.
+- Speed up mcompat_find_chain_vid since we are just going to lower voltage
+during tuning it doesn't need to be very accurate.
+- Fast start/restart with clean shutdown to lower power but powered on mode.
+- Import Dragonmint T1 driver and version rolling AKA asicboost support.
+- Do not give out API description to random probes to inappopriately open ports.
+- Use system provided uthash and libjansson if provided preferentially now.
+- Update uthash to version 2.0.2
+- Avoid potential buffer overflows in api.c
+- Fix write config with enable-icarus changes.
+- Sleep if btcd isn't immediately available and try again when attempting to
+decode a coinbase.
+- Elaborate what type of pool coinbase transactions to decode and do not print a
+warning if no devices are attached when decoding.
+- Add a --decode option that decodes a pool's coinbase transactions and exits.
+- Set up gbt_curl before setting gbt_solo flag to prevent gbt_curl deref in
+longpoll_thread.
+- remove cgminer-api libusb dependency
+- Update jasson library in gitignore
+- Fix compile guide for api-example.c
+- Cleanup libusb-1.0 in gitignore
+- Remove AVA7_FREQ_CUTOFF_MODE for Avalon7
+- Initialize default temp in detect_modules for Avalon7
+- Remove AVA7_FREQ_TEMPADJ_MODE for Avalon7
+- Fix build with disable-libcurl.
+
+
+Version 4.10.0 - 23rd January 2017
+
+- Silence warning.
+- Clean up compac driver.
+- Allocate appropriate memory size for PSECURITY_DESCRIPTOR
+- Remove jansson memory tricks.
+- Increase header size for gbt solo.
+- Increase header size.
+- Fix curl build on mingw
+- Windows build fixes.
+- Fix avalon4 warnings.
+- We can only change diff once per notify so assume successive diffs are stacked
+for successive notifies.
+- Enable avalon4 building with avalon7.
+- Move to system libusb.
+- Upgrade to jansson 2.9
+- display restart thread errno
+- String length sanity checks in config parsing.
+- Make pool_no and block height logging more consistent
+- Always clean swork when prev_hash changes
+- Add nonce mask to api for Avalon7
+- Add new options for Avalon7
+- Add a device table for Avalon7
+- Update AVA7_DEFAULT_MINER_CNT usage
+- Add frequency range support 100MHz - 500MHz
+- Added roll_work_ntime() to enchance performance
+- Revert "Set default ava7 nonce mask to 27 which empiric testing shows stable
+and more rapid rise in frequency without significant overshoot."
+- Remove useless code in avalon7
+- Detect avalon7 modules during ava7 init and only add the device if any modules
+exist.
+- Unplug avalon7 on failing to re-init AUC to allow a clean hotplug event which
+is more reliable.
+- Fix warning without ava4/7
+- Re-initialise libusb if there are no active devices before the next hotplug
+event.
+- Reset the tv_end variable before each libusb handle events call.
+- Fix Vo display for A741
+- Update AVA7_DEFAULT_ASIC_MAX for A741
+- Tweak ava7 fan control further.
+- Set default ava7 nonce mask to 27 which empiric testing shows stable and more
+rapid rise in frequency without significant overshoot.
+- Set ava7 starting fan to 1/3 speed between min and max.
+- Convert the avalon7 fan control to use a PID-like feedback mechanism for
+optimal fan speed and quasi-constant temperature. Change meaning of target
+temperature to actual desired temperature, setting it to 90C. Set minimum
+fanspeed to 5% as a safety fallback.
+- Inherit the diff from the original stratum in submit_nonce2_nonce, fixing
+wrong diff submits on avalon6/7.
+- Use diff1 and diff rejected to calculate hashrate in avalon7 for a more stable
+displayed hashrate.
+- Check for nodev errors after usb transfers to not change the err value.
+- There is no need for complicated locking in usb_reset; simply use the write
+lock and avoid locking risks.
+- Update nonce mask from 31 to 29
+- Send jobid when it's update
+- Flush useless works when new block was found
+- Update error polling count
+- Fix frequency mode for Avalon7
+- Added connection overload detection and API entry
+- Fixed some English mistakes
+- Fix display mm status when stratum pool is failed
+- Update api display
+- Reset usb devices if they fail to initialise.
+- TIMER_ABSTIME is not available on osx
+- Get rid of binary stratum work based on avalon7 define.
+- Remove impossible comparisons from avalon7.c
+- Style police.
+- Don't use the stratum work generator for Avalon7
+- Add STRATUM_USER_AGENT macro for utils
+- Update README and ASIC-README for Avalon7
+- Add Avalon7 support
+- Fix warning in AV4 driver
+- Update AVA4_DEFAULT_MODULARS
+- Update nonce2 start and range
+- Add more options for AVA4_FREQ_TEMPADJ_MODE
+- Double check data size when use avalon4_auc_xfer
+- Update AVA4_MM60_TEMP_FREQADJ
+- Fix xfer_err_cnt
+- Avoid thread block when use AV4 iic driver
+- Update check rules for AUC
+- Support AV4 miner detect through iic
+- Fix some settings for AvalonMiner 6.0
+- advertise segwit support for solo mining
+- add support for segwit commitment insertion
+- add bip9 support
+- Fix work update for hash_work
+- Fix various minor once-off memory leaks
+- Fix read beyond stack size issue
+- Reattach kernel driver to correct IF
+- Fix clobbered by ‘longjmp’ or ‘vfork’ warning
+- Fix unreachable code
+- allow for independent target freq so bad stick doesn't effect group
+- +1 freq hex alignment
+- tidy up naming to match general conventions
+- tidy up some unused vars
+- Add in ramp code. Untie compac detect from default icarus fallback detect to
+have a cleaner device detect path.
+- move over code to fit serial number on screen
+- changed a few spaces to tabs
+- Added GekkoScience Compac detection and support to the icarus driver.
+- Show Current Block Version under pools api
+- Show Current Block Height under pools api
+- Update avalon4 options
+- Update AVA4_DEFAULT_SMART_SPEED to AVA4_DEFAULT_SMARTSPEED_MODE3
+- Update avalon4_update process
+- Code cleanup
+- Minor update
+- Detach the duplicate dev when detect in xfer
+- Update mm count when scan hash
+- Check module if exits when detect
+- Update hashmeter for avalon devs
+- Fix i_5s usage
+- Update smart frequency for AVA4_DEFAULT_SMARTSPEED_MODE3
+- Add new smart speed mode (mode 3)
+- Update default value
+- Add more options for smartspeed
+- Don't label threads of devices that are paused as sick
+- Set avalon4 to cut off if thread should be paused
+- ASIC-README: Avalon6 will come up as AV6
+- Update the ASIC-README and help message of Avalon4/4.1/6
+- fix cgminer-api compilation on osx
+- Support low diffs like those on testnets
+- Restart being missed outside of block change
+- Set default frequencies for ava4/6 in the correct place if none is specified
+- Use a generic zero stats function for drivers that don't implement their own
+- The fan control for ava6 overshoots often so use a safer non-linear curve
+mechanism relative to the target temperature and remove inappropriate caps to
+opt_avalon4_overheat and opt_avalon4_temp_target
+- 450 would appear to be the most reliable startup speed for ava6 so revise it
+down further
+- Avoid double locking when checking if a block exists to prevent a further race
+between seeing it and adding it
+- AVA6 is more reliable starting up at the slightly slower speed of 470, making
+it internally start at 450 before working its way up
+- Set the freq array variables when setting default frequency as well
+- Set default frequency according to ava type if none is specified on the
+command line, choosing 475 as default for ava6
+- Use AV6 name for avalon6 in ava4 driver
+- Update copyright notices for ava4 driver
+- Fix warnings
+- Log blockheight when the pool changes to a new block.
+- Check for stratum clean message in test_work_current to not give false
+positive messages for missed pool notifications of block changes
+- Update AVA4_DEFAULT_SPEED_ERROR
+- Add new option for Avalon6
+- Update frequency process
+- Update AVA4_DEFAULT_TEMP_TARGET for Avalon6
+- Update voltage decode for Avalon6
+- Update temperature target and overheat
+- Update target temp settting
+- Update fan adjust
+- Update Vol api display
+- Update avalon4 option
+- Display stratum difficulty in pool api
+- Fix divide by zero
+- Fix nonce counter
+- Fix GHS display
+- Display total asics for Avalon6
+- Update max diff for Avalon4 and Avalon6
+- Update temperature check
+- Fix typo
+- By default we enable automatic frequency
+- Minor changes
+- Update display
+- Update api display
+- Fix GHSmm
+- Update api for Avalon6
+- Display pll infomation for Avalon6
+- Update frequency for Avalon6
+- Turn on avalon6 nonce check as default
+- Update convert voltage
+- Update voltage display
+- Display more status
+- Enable adjust frequency automatically
+- Update convert voltage
+- Update temperature display for Avalon6
+- Add more options for Avalon6
+- Update asic count for Avalon6
+- Update hashrate counter
+- Decode voltage and temperature for Avalon6
+- Support error code for Avalon6
+- Support 2 miners for Avalon6
+- Show ASCI status for Avalon6
+- Don't need config voltage for Avalon6
+- Update MM60 profile
+- Add nonce counter for Avalon6
+- Hide MW for Avalon6.0
+- Count hw for Avalon6
+- Increse stat buffer size
+- Fix job_idcmp when copy stratum
+- Add Avalon6 support
+- Apply immediately the first set_difficulty
+- store the next difficulty
+- Make pool fallback time configurable and default to 2 minutes instead of 5.
+Rework fallback mechanism to check pool status every 5 seconds and not miss a
+recovering pool.
+- Fix error message for pools such as p2pool that use no nonce1 in their stratum
+templates
+- Set work delay to 1 for AntS1 and AntS2
+- driver-bitmain default tempoverctrl off so it can be disabled
+- API add Work Difficulty to pools
+- remove some documention references to getwork
+- Support multiple modulars frequency setting
+- Decode frequency for Avalon nano 2.0
+- Decode adc status for Avalon nano 2.0
+- Display temperature in api (Avalon nano 2.0
+- Display asic match works for Avalon5
+- Support adjust frequency by asics
+- Display asic freq in status
+- Diplay moving average dh for Avalon5
+- Fix api display
+- Upgrade date/timestamp strings to millisecond accuracy
+- Support voltage adjustment automatically by modular
+- Support Avalon5
+- Increase AVAM_DEFAULT_ADJ_INTERVAL
+- Boundary check
+- Update AVAM_DEFAULT_VOLTAGE_MIN
+- Check the final freq
+- Support frequency adjust automatically
+- Add simple moving sum of the hardware errors
+- Add elapsed info for Avalon miner
+- Advise against building blockerupter driver
+- Move blockerupter driver to after icarus to not interfere with their detection
+if built in
+
+
+Version 4.9.2 - 12th June 2015
+
+- Only blacklist devices on iManufacturer if they don't have an iProduct
+- Simplify the pool work choice switching now that only local work generation is
+supported
+- Check for absense of stratum notify on stratum pools to see if they're usable
+- Remove getwork support
+- Remove test for localgen since all work will be
+- Deprecate failover only option in anticipation of removing getwork
+- Deprecate expiry option and fix it to 10 minutes, replacing all deprecated
+config calls in the API with a deprecated message
+- Deprecate scan-time which is irrelevant with ASIC speed mining
+- Deprecate opt_queue since all mining is now local work generation which is
+rapid and it serves no useful purpose to generate work that is potentially
+stale, increasing CPU usage to check it before using it
+- Change default au3 voltage to 775 to more reliably run at default frequency
+- Set correct timeout for AU3 in icarus_detect_one though it won't affect
+functioning
+- Blacklist LIX device which doesn't exist
+- Round up fail time to next highest integer on icarus
+- AntS3 - set default work delay
+- add pool block quality to api
+- AntS3 - redo work send/stats and add a tuning option - not yet tuned
+- Ant - ignore useless options in case they are passed
+- AntS3 - initial S3 merge basic support
+- Ant voltage isn't S1
+- API - restore IPv4 address .0 padding and update copyright dates
+- ants2 - separate freq from options
+- ants2 - make it compile :p
+- ants2 - add voltage
+- ants2 - remove libusb/udev dependency since it doesn't use USB
+- AntS1 ensure correct endian and correct wid
+- libsystemd: Notify watchdog
+- libsystemd: Notify run state and status line
+- Catch SIGABRT signal and exit gracefully
+- Add libsystemd configure option (default disabled)
+- Document --with-system-jansson configure option
+- SP30: Fix logging to use applog over printf calls
+- Allow building with system jansson
+- Set request ID in validateaddress JSON-RPC request.
+- Cope with reconnect being sent a port number as an integer or string
+- Added syslog mask to limit amount of output
+- regressed with "BAL" little-jalapenos
+- Add yet more whitelisting of BMA devices, removing the case insensitive match
+for manufacturer
+- Fix detection of no mining device configuration.
+- Remove LT_INIT from configure which breaks ltmain.sh detection for some
+automagic reason
+- Add missing hashtable_seed file
+- Add missing file
+- Speed up shutdown by decreasing usb poll time to 100ms and using no timeout on
+exit
+- Cope with attempted reuse of strings in input_pool
+- Manage failed URL entry at startup more gracefully, adding stratum+tcp://
+automatically if http:// isn't specified
+- Fix various symbol size change warnings
+- Import jansson updates from ckpool
+- Check for attempts to copy to/from null in cg_memcpy
+- Use cg_memcpy throughtout bitfury driver
+- Use cg_memcpy throughout cgminer.c and util.c
+- Use alloc helper in miner.h
+- Use alloc helpers in bitfury driver
+- Use alloc helpers in spondoolies drivers
+- Use alloc helpers in bflsc driver
+- Align_len in all alloc helper calls
+- Use align_len in all alloc helpers
+- Use alloc helpers in usbutils
+- Use alloc helpers in icarus driver
+- Use alloc helpers in avalon4 driver
+- Use alloc helpers in api.c
+- Use the alloc helpers in util.c
+- Convert all users of alloc to use the helpers in cgminer.c
+- Add helper functions to various alloc functions that automatically check for
+failure
+- Fix rare dereference error on pool stratum queue
+
+
+Version 4.9.1 - 3rd February 2015
+
+- Fix various unused warnings
+- Fix avalon4 warnings
+- Display notice if pool successfully negotiates stratum resume
+- Support auto adjust voltage individually
+- Don't keep retrying to connect to a pool that has been removed
+- Null the actual pointer used to call discard and free_work and safely handle
+being called with a null pointer, giving a verbose warning about the call site
+- Fix off by one error when running out of queued IDs in bflsc28_queue_full
+- Uninit BET driver when it fails to initialise any boards
+- Fix detection of butterfly labs sc 65nm devices with the addition of the 28nm
+device imanufacturers trumping their detection
+- Remove compilation warnings. Cast overly-specific #def'd values to the
+destination type, unsigned int.
+- Basic ability to compile and run on FreeBSD 10.  Only tested compilation with
+avalon, avalon2, bflsc, and icarus; only tested functionality of bflsc and
+icarus.
+
+
+Version 4.9.0 - 16th December 2014
+
+- Minor fix
+- Fix MM41 voltage setting
+- Fix the default settings of new module
+- Count non matching stratum as a hw error on ava4
+- Fix ava4 build incompatibilites and missing write config parameters
+- Use strcasecmp for device matching in usbutils in case of subtle manufacturer
+changes
+- Add manufacturer and product definitions for ava4
+- Cosmetic ava4 change
+- Cosmetic ava4 message fixes
+- Add sanity check for NULL data being passed to usb_perform_transfer
+- All write errors should be treated as fatal for ava4 devices
+- Change initial fan start speed, mins and max for avalon4 to ensure fan starts
+spinning but can go lower RPM
+- Disable zero length packets on ava4 before trying to init
+- Add a cgpu device option to disable zero length packets and enable it for
+avalon4
+- Display ava4 stats consistent with other devices
+- Add ava4 to udev rules file
+- Fix build warnings on ava4
+- Fix ava4 build
+- Add Avalon4 support
+- Filter duplicate stratum shares from being submitted upstream
+- Do rudimentary detection of duplicate shares per device
+
+
+Version 4.8.0 - 25th November 2014
+
+- Allow forcing of building driver combinations with --enable-forcecombo
+- Put spaces between name and id in avalon2 and icarus
+- Relax detection of a failing ava2 to more than 1 minute and perform the test
+after polling for results
+- Cap maximum diff on ava2 in order to still get shares
+- Put space between device name and id to prevent device names with numbers in
+them confusing the display
+- USB write errors are always fatal so they should be treated as such on ava2
+- Issue a usb reset for ava2 that is not returning valid shares and then drop it
+if it persists for over a minute
+- Process share results without a result value
+- Damp out hashrate displayed for antminer USBs
+- Add voltage and speed where relevant to antminer USBs
+- Don't estimate time on any antminer usb during a timeout
+- Return icarus nonce ok only when the nonce size matches the device or more
+- Don't discard old workids until we cycle back to them on antusb and look for
+more nonces in the buffer
+- Adjust ant usb timing for queued work
+- Use a cyclical list for the ant queued work
+- Mask and limit workid for antusb and dont clear buffer
+- Check the nonce on the worked item, not the submitted work
+- Skip over unfinished work that we can't free in ant usb
+- Use a workid and array if possible for the small ant usb work queue
+- Create an array for antworks for antminer usb devices
+- On U3 calculate hashrate purely on shares, not timeouts
+- Add switches for AU3
+- Adjust icarus wait timeout according to device
+- Differentiate U3 from U1/2 as a separate driver with different parameters and
+adjust timing accordingly
+- Skip ANUs detected in rock detect
+- Try U3 after trying other icarus options
+- Add rudimentary ANU voltage setting support for U3
+- Fix ignoring unprefixed v6 address in api allow list
+- Fix minor typos in Spondoolies SP10 and SP30 drivers
+- Implement a basic rock_flush function to discard the base work we are rolling
+work from.
+- Task_no for rockminer from the nonce bin should simply be masked
+- Change rbox default correction times to 5 in a revised frequency order
+- Change default frequency on T1 to 330
+- Reinstate last received check and resend in rockminer, being more lenient at 2
+seconds to allow for dither errors at 1
+- Roll work for the rbox when possible
+
+
+Version 4.7.1 - 4th November 2014
+
+- Selectively yield on dropping a lock only on single CPU platforms
+- Make it impossible to configure in more than one device that is meant to be
+standalone. Add more information to configure help, along with comments for new
+drivers.
+- Add warning against system libusb in configure help
+- stratum_rthread sleep only 3s when all the pool have disconnected
+- Filter responses that don't have a result
+- Implement support for pool ping and json integers of zero in getversion and
+ping
+- Fix segfault when writing config with hashratio built in
+- Save pools in priority order at time of writing config
+- Set the correct flag for close on exec for sockets
+- Suspend stratum on removing a pool
+- Set CLOEXEC on sockets on linux
+- Drivers that take a diff should specify a max diff or it is assumed they don't
+support one so set max_diff to 1 if unset
+- Send hfa generic frame only if voltage was specified on the command line for
+that device
+- Set hashfast voltage settings only when really needed
+- Hashfast voltage support
+- Increase max diff on sp30 to 1024
+- Reset ipv6 flag to false in every api-allow loop
+- undeclared identifier 'IPV6_ADD_MEMBERSHIP' fix for apple
+- two back temps spondoolies2
+- two back temps spondoolies
+- correct suggest_difficulty json rpc call
+- Add more usb3 hub identifiers for windows
+- Set driver max diff to large value if unset
+- Wake gws on get queued
+- Implement blacklisting of attempting to match known products from ones without
+identifiers
+- Fix hfa driver building without libcurl
+- Enable building libusb without udev
+- Fix off by one calculation error in sp30 leading zeroes
+- Send correct diff work to sp30 for hashmeter to be correct
+- Do the sleep in spondoolies_queue_full_sp30 after dropping the lock
+- Minor tidy in sp30 driver
+- Fix sp30 warnings
+
+Version 4.7.0 - 14th October 2014
+
+- Implement generic inet_pton for windows
+- Fix warnings
+- Fix bulk of remaining style in blockerupter.c
+- Tidy style in blockerupter.h
+- Tidy bulk of style in blockerupter.c
+- Fix missing minimum diff setting for blockerupter
+- Fix unused variable warnings
+- remove unnecessary sleep; fix potenital div by 0 errs; use min_diff in driver
+definition
+- Fix coding style
+- Make the sp30 hashrate meter based on valid share generation
+- Change default max queue back to 1 in line with speed of most current asic
+controllers
+- Change diff limits to values suitable for sp30
+- Add pool number to response from addpool to the API
+- Make the restart and quit API commands valid json responses
+- Fix number of nos
+- Add option to set clock ('--bet-clk X' actual clock is (X+1)*10 )
+- compatible with X24 board
+- Fix error when using v6 without mask in api-allow
+- Support ipv6 multicast
+- Set min_diff to 1
+- Allow arbitrary clamping of lower device diffs for slow controllers by driver
+- Don't set default fan to max on hashratio
+- The 2nd read never gets anything on ava2 so remove it entirely and just return
+an error if we are out of sync
+- Implement support for mining.suggest_difficulty
+- Fix client ip address output
+- Free addrinfo garbage
+- Remove the brackets when using v6 pool address
+- Add ipv6 support for api listen
+- Avalon Nano: Add support Avalon Nano usb miner
+- fix bug in setdiff
+- limit minimum diff to 64
+- Add BlockErupter Driver
+- Avalon2: display currect max temperature on statline
+- Remove unused variable
+
+
+Version 4.6.1 - 20th September 2014
+
+- Throttle bflsc28 devices when they hit the overheat limit
+- Add whitelisting of firmware used in final bflsc28 products
+- API.java - remove lowercase of all data sent
+- Avalon2: Add 3 bytes nonce2 support
+- Avalon2: MM needs n2size length <= 4
+- Use fan min as fan speed when run with --avalon2-fixed-speed
+- Clear the pool submit fail bool after adding shares to the stratum hashtable
+to minimise window the share is not in the table
+- api-example unlimited socket works
+- Add custom strcasestr and use custom gnu type functions in bflsc
+- Fix windows build of bflsc driver
+- Fix possible deref in bflsc28
+
+
+Version 4.6.0 - 7th September 2014
+
+- We should not be checking for pool_unworkable in cnx_needed as it is keeping
+stratum connections open on unused pools
+- Properly handle lack of input when adding pool via menu
+- Allow workers without passwords
+- minion - increase max chip number
+- Avalon2: add more comments on Avalon2 options
+- Avalon2: add polling delay option, long coinbase support, LED status on API,
+change overheat from 88 to 98
+- minion - add a ' before non-zero core error counts
+- minion - hidden optional per core nonce stats
+- bflsc28 - clock is hex
+- bflsc28 - allow setting clock and volt from the API ascset command
+- bflsc28 - add chip count to stats
+- bflsc28 stats
+- Simplehacks to better serve bflsc28
+- Only use one hashtable for bflsc28 work queued
+- Copy back the buffer after we've stripped the inprocess field on bflsc
+- Parse results for BMA based on uid and remove work from the queue when found
+- Strip out the inprocess details from bflsc results if it exists
+- Create a hashtable of work by uid as it's accepted by BMA
+- Add some rudimentary values for BMA sleep times
+- Fix various errors in queueing work for bflsc28 and limit job queueing to 10
+to fit within a usb frame
+- Create preliminary work queueing for bflsc28 using jobs of up to 20 at a time
+by rolling work where possible
+- Convert all bflsc transfers to the full 512 bytes
+- Don't mistake bflsc28 for fpga
+- Do initial detection of bflsc28 devices
+
+
+Version 4.5.0 - 29th July 2014
+
+- Fix windows build for hashratio and ava2
+- Demote bad checksum message in cointerra driver but allow message to still be
+parsed since it won't allow existing firmwares to work otherwise
+- Reorder and document the configure options
+- Merge https://github.com/KnCMiner/cgminer into knc
+- Change default voltage on ava2 to 0.666V because Satan
+- Enable combined building of avalon2 and hashratio
+- Fix stratum embedded fpgas to not duplicate work with other devices
+- Implement smarter PID type fan control for ava2 allowing more generous
+temperatures and far lower fan speeds for optimal power and noise usage. Adjust
+default frequency to 450 as per recommendation.
+- Fix various warnings in ava2
+- Go back to polling design since async will not work for ava2 and fix various
+read design errors
+- Fix error in 2nd read functions for av2 and hro
+- Correct init and read sequence for ava2, and convert from a polling mechanism
+to a separate read thread
+- Initial commit of ava2 conversion to direct USB
+- Display frequency and voltage with ava2 on the statline
+- Store fan percentage and display temp and fan percent for ava2
+- Set avalon2 frequency and voltage to appropriate defaults if none are
+specified on the command line
+- Demote some ava2 messages that don't need to be errors and remove unused works
+array
+- Fix broken fan logic for ava2
+- Fix hexdump on 64bit
+- rockminer frequency is between 200 and 400 MHz
+- fix jansson include path in cgminer-api compile instructions
+- Remove requirement for ifndefs for avalon2 from the generic driver work
+function
+- Fix hashratio device name
+- Make submit_nonce2_nonce return whether the share was valid or not
+- Reinstate missing necessary init sequence for hashratio
+- Handle disconnected hashratio devices
+- Add hashratio frequency command line
+- Fix stratum updates not being passed to hashratio devices and clean up
+- Move to updated avalon2 type driver model for hashratio device
+- Initial import and conversion of hashratio driver to direct USB
+- Increase the internal buffer for API response,   as "stats" command response
+can grow greater than 8K
+- Detach test pool thread only if we have a blocking startup
+
+
+Version 4.4.2 - 17th July 2014
+
+- Remove the use of the pthread_tryjoin_np which is currently unimplemented on
+many platforms
+- Fix processarg parameters loaded from a config file not being saveable
+- We only use the jansson in our source tree so no need for special case
+handling of older versions
+- Upgrade jansson to 2.6
+- Only clear sockbuf if it's been allocated
+- Fix missing osm-led-mode support in write config
+- Deal with nanosecond overflow in both directions on both addition and
+subtration of timespecs
+- Rename sp10 driver internally from spondoolies to sp10
+- minion - add a 2nd (optional - disabled) reset test
+- production stats added, reset queue added
+- minion - correct led ghs2 choice
+- minion - correct ghs2 display
+- minion - reset the led counter when doing a chip RSTN full reset
+- minion - don't reset the led counter for an SPI reset
+- minion - led per chip and use all time average
+- minion - report spi error counts and settings in stats
+- minion - undeclared fix
+- minion - chip power cycle option
+- minion - record 0xff error history and reduce screen output
+- minion - reset on result or fifo 0xff
+- minion - clarify the 0 value of spireset
+- minion - make SPI reset more configurable
+- minion - make the SPI reset ms sleep a parameter and API settable
+- sp10 sensors
+- sp30
+- minion - led+more api setting
+- Avoid blocking all pool testing if one pool fails to ever init
+- There is no point storing the hints addrinfo in struct pool
+- minion - 'reset' SPI when getting errors
+- initialise more pool values in benchmark
+- minion - auto adjust freq
+- merge upstream frequency changes
+- icarus - timing history in its own function
+- rbox - add lotsa stats, tidy up a bit more
+- Fix an off-by-one.
+- icarus - detect stat should be LOG_DEBUG
+- icarus - tidy up rbox code, remove statics, and add rocketbox
+- minion - do an early reset to clear the chip status
+- minion - use descriptive names for the list types
+- Avalon2: automatic adjust fan speed, using crc16 on job_id compare, turn on
+the led by API, detect twice when start, remember the last stratum message
+increase the hashrate, add cutoff option
+- fix AntS1 breakages from AntS2 changes
+- minion - disable dup nonce check
+- minion - add an ioseq number for each ioctl to simplify work ordering
+- minion - redo old work expiry based on txrx order
+- minion - more work stats, minimise queued work, free flushed queued work
+- minion - allow resetting a chip via the API
+- minion - correct 'WQue Count' in stats
+- minion - delay after reset, reset history also, add dups to api stats
+- noncedup - give access to the internal stats
+- minion - increase reset to 75%
+- minion - dup checking, disable reread by default and extra ioctl debugging
+- minion - always check the chip queue before queuing new work
+
+
+Version 4.4.1 - 21st June 2014
+
+- Move icarus driver to being seen as an asic
+- Clear usb reads on each pass through icarus detect to hopefully prevent false
+positives for detecting rboxes
+- Clean up pool failure and failover code for stratum
+
+
+Version 4.4.0 - 16th June 2014
+
+- Tidy unused rockminer variables
+- Tidy rockminer defines
+- Make rockminer driver compatible with other icarus drivers being present
+- Import basic rbox driver
+- minion - add optional (on) GPIO chip selection
+- Clear the pool idle flag in the pool test thread
+- CoreFmatch in cointerra should be a uint16
+- Display error message if we receive one on share rejects
+- Allow zero length strings to be passed to valid_hex
+- delete unused roundl definition
+
+
+Version 4.3.5 - 10th June 2014
+
+- Cointerra driver updates.
+- Sleep before retrying in the test pool thread after a pool has died
+- Use valid_ascii testing for job_id since it need not be hex only
+- Only show slow/down message till pool is flagged idle
+- Do some random sanity checking for stratum message parsing
+- Keep looking for when a pool comes to life at startup and touch the logwin so
+the message is not invisible
+- Fix no libcurl build
+- Added Drillbit Thumb to udev rules.
+- Avoid dereference on getting API stats on partially initialised HFA instances
+- A1: add support for updated product variants, small fixes
+- Add one more usbutils fix
+- Convert uses of usbutils memcpy to cg_memcpy
+- Add a sanity checking memcpy function which checks for overflows
+- minion - count force use reread
+- minion - add a disabled ioctl() test
+- minion - add more checking of SPI results for corruption
+- minion - optional (disabled) ioctl() debug
+- Increase S1 overheat to 75 degrees C
+- Add ruby api-example to API-README
+- minion - allow core selection at runtime
+- API - lcd all-in-one brief summary
+
+
+Version 4.3.4 - 25th May 2014
+
+- Add support for 2 nonces per block in spond driver
+- Increase timeout on reset in cta driver to 5 seconds
+- Increase max diff on spondoolies driver slightly to be well below spi comms
+limitations
+- Use the active contents lock and safe list iteration within the linux usbfs
+code
+- Add Ruby Api Example
+- Automatic detect the small miners
+- Update default modules from 3 to 4
+- Fix the temp max. we should use currect max temp
+- add avalon2-cutoff options
+- Enable the cutofftemp to Avalon2. ignore longer coinbase and longer merkles
+stratum
+- Fix the diff value used on MM firmware
+- Mark pool as idle if stratum restart is failed
+- Add hacky workaround for double list removal race in libusb
+- Make the work given in benchmark mode deterministic on a per-device basis
+- Rework the benchmarking code to use a deterministic set of work items with a
+known number of diff share nonces at regular spaced intervals
+- minion - restrict nonce read result size to ioctl() limit
+- minion - must check temp when overheated
+- minion - idle chips that hit >100C until back to 80C
+- minion - report the chip/reg when aborting due to an invalid ioctl() size
+- minion - all freq in Mhz but only convert when used
+- minion - remove unused ioctl debug
+- minion - command queue is now larger
+- minion - check rolled in stale work cleanup
+- Work stats should be based on device_diff not work_difficulty since non-shares
+haven't been filtered out yet
+- Prevent a segfault when writing a config file containing 'rotate' option
+- minion - comment out HW debug message
+- minion - roll work to reduce CPU
+- minion - report init_freq in stats
+- api - howoldsec is only used for USB
+- minion - allow setting the frequency
+- minion - disable iostats by default since it slows down mining
+- minion - define frequency value table
+- minion - report temp/cores/freq and handle temp formatting
+- minion - item is undefined
+- Rationalise diffs stored in the work struct and document them to avoid further
+confusion
+- Add basic API stats for nfu drivers to see how many submits each chip returns
+- Add output direction for the EN0 pin on nfu driver
+- Support power management optimisations in newer nf* firmware
+- Support variable numbers of chips with NFU and BXM drivers
+- Identify number of chips in nanofury devices and change name accordingly
+- Rename nf1 driver to nfu in anticipation of support for more chips
+- Make hashfast reset counter rise on old instances when inheriting the value on
+new ones
+
+
+Version 4.3.3 - 3rd May 2014
+
+- Fix typo
+- Work should be freed when aged, fixing a massive memory leak for bxf devices
+- miner.php fix single rig summary/config field formatting
+- miner.php fix single rig total formatting
+
+
+Version 4.3.2 - 2nd May 2014
+
+- Fix accounting bug with nrolltime drivers
+
+
+Version 4.3.1 - 2nd May 2014
+
+- upgrade some int to int64_t to avoid overflows in reporting
+- Make reconnection messages more explanatory
+- Stratum client.reconnect require matching URL
+- Fix memory leak in submit_noffset_nonce
+- Clean up any work that may not have been used in the work scheduler
+- Avoid unnecessary deref now that it's done within discard_work
+- Clean work pointers after one way usage functions
+- Avoid unnecessary total_work_inc in generating local work
+- Cosmetic fixes
+- Fix idle bug, when redirected client can't auth
+- Rename spond temp rate to asics total rate
+- Build fixes
+- Set the unique id only for usb devices with serial strings longer than 4 chars
+long
+- Use usb serial strings as unique id if devices have them
+- Discretely identify the onestring miners as OSM
+- Add bxf debugging option and osm led modes
+- A1: modularize board selector / add initial CCR support
+- A1: cleanup tca9535 logging
+- A1: fix and extend PLL parameters
+- A1: clean up compile warnings
+- A1: use real level in hexdump
+- Add identification for onestring miner variants
+- Avalon2: Parser the power good signal
+- driver-avalon2: this functions used on detect, which don't have thr setup yet
+
+
+Version 4.3.0 - 18th April 2014
+
+- Put sleep in spond hash instead of queue full function
+- Remove unused function for when compiled without curses
+- Fix typo
+- Add temperature rate, front, rear and device temperature to spond API output
+- Limit bxf sleep in bxf_scan to 100ms minimum for strings of many chips
+- -Werror=format-security error on driver-bitmain.c
+- Fix parameters passed with getblockhash
+- Check the block hash with the proper command when looking for orphan chains
+- syslog requires a facility ... in more than one place
+- Shuffle windows headers included
+- Adjust the bxf sleep time according to the number of chips detected
+- Fix off by one error in bxf chip count when adjusting device size
+- Recalloc correct pointer
+- Make instructions associated with winusb error even more explicit
+- Add midsing headers to cgminer source in Makefile
+- Trivial style changes to mg proto parser
+- Trivial style and warning clean ups on spondoolies driver
+- Merge spondoolies driver patch
+- Call any BXF device with 3-6 chips reported HXF
+- Avoid derefrence when calling statline before on hfa device during init
+sequence
+- Calloc the info structures even on failed hfa reset to prevent later possible
+dereference
+- Load all hfa devices based on identification alone and defer init sequence
+till mining thread init sequence to allow all devices to be recognised rapidly
+but each device initialisation not delay others
+- Do not do thread shutdown unless thread init succeeded
+- Remove unnecessary check for thread_prepare function
+- Recognise variations on BXF based on chip value returned in responses
+- Provide helper function for recallocing memory
+- syslog requires a facility
+
+
+Version 4.2.3 - 3rd April 2014
+
+- Decay the per device hashrates when only the watchdog is calling the hashmeter
+- Fix parsing of config files failing on custom parsing
+- Allow an arbitrary number of chips in the BXF driver, showing results from
+each chip in the API and identify the hexfury, naming it HXF
+- Disable toggling display by default and offer a --widescreen option to have
+all the information on an extra wide display.
+- Use OPT_WITH_CBARG for all custom parsing functions to allow their values to
+be written generically when writing the config file from the menu.
+- Provide a ccan variant OPT_WITH_CBARG that assigns the arguments passed as a
+string and then performs the callback function on the string.
+- Define strings to store special option parsing parameters leaving no
+OPT_WITH_ARG missing args
+- Correct the writing of special case options to the config file
+- Provide support for writing anu freq from menu write option
+- Update to diver-avalon2.c
+- Generalise a lot more of the command line options simplifying the write config
+function and making it write far more values unaided
+- Use the general opt_set_charp functions for setting api parameters
+- Json escape any strings written to the config file
+- Store standard charp options when writing config files
+- Add support for all the integer range options when writing the config file
+from the menu
+- Remove the --device and --remove-disabled options which don't work in a
+meaningful way any more
+- Make the bxf bits configurable on the command line
+- Provide a --btc-sig option to optionally add a custom signature to the solo
+mining coinbsae
+- Compact gbt solo extra data and store the length, allowing it to be variable,
+leaving room for a signature
+- miner.php - Kano summary Pool Acc/Rej should be only work submitted
+- miner.php add best share and gen formatting for pool summary
+- miner.php - remove BGEN/GEN eval() errors from the web log
+- miner.php allow optional fields when gen is disabled
+- miner.php dont format missing gen fields
+- miner.php make Summary a custompage
+- miner.php allow uers and system lists of customsummarypages and add more
+examples
+- Fix getwork share submission
+- Cosmetic fix to udev rules
+- Put WU on the hashrate status to compact lines further
+- miner.php show api/rig errors at the top of a customsummarypage
+
+
+Version 4.2.2 - 29th March 2014
+
+- Minor correctness fix for unnecessary free
+- Clean up various curl build issues
+- allow url based config files
+- Frequency only needs 3 digits for cointerra statline
+- Use the serial number as unique_id for cta display
+- Make it possible to enable/disable the status window from switching via the
+display menu
+- We should not update the tv hashmeter time unless we're updating the hashrates
+- Add cointerra devices to udev rules.
+- Use hashfast unique id instead of number since the unique id is displayed
+- Remove displayed space
+- Left align the displayed unique id
+- Use the hashfast opname as its unique identifier
+- Display BF1 serial number as its unique identifier
+- Display a unique identifier instead of a number if the device has one
+- Use an alternating status display to return to a compact width of 80
+characters, allowing more information to be displayed.
+- No need for looking for khash hashrates in summary any more
+- Fix two potential minor mem leaks
+- Fix memory leaks in setup and generate work for gbt solo.
+- Fix off by one malloc size error
+- Fix memory leak in update_gbt_solo
+- Put sanity check on decay_time to prevent updates with no time
+- Add 3 rolling average hashrates to API output for summary and devs.
+- Use the extra status screen real estate better, displaying rolling 1/5/15min
+average hashrates as well.
+- Revamp the ageing crufty hashmeter code to have proper exponential decaying
+values and store rolling 1/5/15min hashrates.
+- Increment total_work under control lock.
+- Trivial variable reuse
+- Add support for other usb3 hubs on windows
+
+
+Version 4.2.1 - 24th March 2014
+
+- Fix various ava2 build issues generically
+- Minimise the amount of heap memory allocations/frees when submitting gbt
+shares.
+- Make varint in gbt submission a stack object.
+- Fix big endian problems with gbt submissions.
+- Fix 32bit overflow on relative diff shown.
+- ants1 - stop results read hard looping
+- ants1 - slow down mining if overheat occurs
+- miner.php allow gen before (bgen) and after (gen) grouping
+- Change default solo mining to failing when no btc address is specified.
+- Use upgrade cglock variants in get_gbt_curl
+- Provide a cg_uilock to unlock the intermediate variant of cglocks.
+- Use the one curl instance for all gbt solo operations, protecting its use with
+a bool set under gbt lock.
+- Only start block detection with gbt solo if setup succeeded
+- One less block detection message
+- Toss out the curl handle after each solo poll
+- Don't reuse any curl handles for solo mining and break out of the lp thread if
+the pool is removed.
+- Make sure to only start the lognpoll thread once on gbt solo.
+- Don't keep RPC connections open for solo mining since bitcoind doesn't like
+having many persistent connections.
+- GBT solo pools should be considered localgen pools.
+- miner.php - speed up formatting and allow calc on gen fields
+- Always show the address we're solo mining to to avoid confusion for when no
+address is set.
+
+
+Version 4.2.0 - 18th March 2014
+
+- Fix missing htobe16 on windows and meaningless >u32 string warning.
+- Software ntime roll for all hashfast devices.
+- Silence harmless warning.
+- Drop a failed restart icarus device to allow it to be rehotplugged if
+possible.
+- Work with more than one transaction.
+- Kill gbt solo pools that don't respond to the gbt request 5 times
+sequentially.
+- Fix ser_number for no remaining val byte.
+- Create a work item and stage it when updating the gbt solo template to allow
+new block detection and restart code to work.
+- Test block hash as well as block height when solo mining to ensure we haven't
+been mining on an orphan branch.
+- Fix transaction processing for gbt solo.
+- Encode height using integer varint format.
+- Make new block detection message not show in gbt solo from test_work_current
+- Add block detection via getblockcount polling in gbt solo and update gbt
+template every 60 seconds.
+- Iterate over transactions twice to malloc only once when copying all the
+transaction data.
+- Update solo coinbase regularly and submit as gbt work
+- Only show merkle hashes for solo mining in debug mode.
+- Set correct flag for solo work.
+- Generate gbt solo work emulating stratum work construction.
+- Set the diff as a double sdiff from gbt solo data.
+- Move swork.diff out of the stratum work section to be shared as sdiff.
+- Generate a header bin from gbt solo as per the cached stratum one.
+- Store strings similar to stratum's when decoding gbt solo
+- Avoid allocing and freeing stratum strings that should be fixed length.
+- Run parser through detect_stratum after stratum+tcp:// is force added
+- Remove unnecessary header length calculation for stratum header binary and
+only binary convert the correct length of the header.
+- Share more fields between stratum and gbt
+- Share coinbase_len variable b/w stratum and gbt and setup more gbt solo
+parameters.
+- Generate a valid coinbase and set nonce2offset for gbt solo
+- Move scriptsig header bin conversion to setup gbt solo
+- Create our own custom scriptsig base.
+- Add helper functions for creating script signature templates and beging
+building template.
+- Do gbt solo decoding under gbt lock.
+- Add more gbt variable decoding from gbt solo information.
+- Store all the transaction data in binary form when using GBT
+- When setting up solo mining, check validity of bitcoin address against
+bitcoind
+- Make pooled GBT mining use merkle bin optimisations slated for solo mining.
+- Abstract out the merkle bin calculation for gbt solo
+- Implement efficient merkle tree base from solo GBT information.
+- miner.php custom formatting and row counter '#'
+- Drillbit: Fix for underestimating hash rate from Bitfury devices
+- Send per-core hashrates at regular ~5min intervals back to cta devices.
+- Calculate the cta per core hashrate at 5 minute intervals.
+- Check the bits of the correct core in cta bit count.
+- Display the bit count along with the bitmap for each cta core in the API stats
+output.
+- Store and display the per core hashrate on cta relative to each work restart.
+- Decrease the time we wait for unsetting a core on the cta bitmap to correspond
+with the lower max diff of 32.
+- Set max diff on cointerra devices to 32 which is still only 11 shares per
+second but allows for earlier confirmation of per core hashrates.
+- Keep track of when the last restart and work updates were triggered and
+provide helper functions for knowing the time since then.
+- hashfast make api stats field names unique
+- Fix gcc longjmp warning in api.c
+- Add a per-core hashrate to the cta API stats.
+- miner.php support edevs and estats
+- API - put edevstatus where it was supposed to be
+- Icarus - allow timing mode to work with ANU and not slow it down
+- drillbit - remove warnings
+- drillbit - minor code tidy up
+- Drillbit: Change language around 'void' to warning about limiter disabled
+- Drillbit: Fix accidental over-counting of HW errors
+- Drillbit: --drillbit-auto parameter for tweakable custom tuning of ASIC speeds
+- Drillbit: Output warning if board reports void warranty
+- Drillbit: Add Avalon & drillbit-autotune notes to ASIC-README
+- Drillbit: Limit work sent out to 8 units in a single pass, was DoSing a full
+double scroll
+- Drillbit: Move drillbit_empty_buffer calls to only when errors occur, were
+limiting performance on Windows
+- Fix Windows bug with libusb_reset_device returning SUCCESS for disconnected
+device
+- Drillbit: Fix some warnings
+- Drillbit: Add --drillbit-autotune option for device to dynamically alter clock
+speed
+- Drillbit: Fix typo in previous commit
+- Drillbit: Remove default config in cgminer, rely on defaults in firmware
+- Drillbit: Combine split USB transfer for sending new work, reduce overhead
+- Drillbit: Add support for protocol V4, with device-agnostic board
+configuration data
+- Drillbit driver: Add support for Avalon-based Drillbit miners
+- API - add edevs and estats - to only show enabled devices
+- Check device data exists on a hfa instance before trying to reinit it.
+- Print off what quadrant regulator failed if known in hfa driver.
+- Reset all the stats on autovoltage complete in cta driver.
+- Use correct diff instead of diffbits in cta driver.
+- Whitelist all firmwares <= 0.5 on hfa for software rolling of ntime.
+- Avoid a memory leak by reusing the ntime field when rolling stratum work.
+- Clear the pipe bitmap on cta only when no share has occurred for 2 hours
+instead of 1.
+- Cta share_hashes should be added, and we can base it on device wdiff instead
+of pool work difficulty for more accurate hashrates.
+- Since the device runtime is now reset, the Raw hashrate entry in the cta API
+output is no longer meaningful.
+- Look for autovoltage returning to zero on cta driver and reset stats at that
+point since the hashrate is unreliable till then.
+- ants1 - cgminerise applog calls
+- Default to stratum+tcp:// on any urls that don't have a prefix instead of
+http.
+- Trivial cta style changes.
+- ants1 - fix/enable temperature checking and remove unneeded temp_old
+- ants1 - move local cgpu variables to info structure
+- ants1 use a klist to store work and copied work
+- Simplify dramatically the cross-process cgminer locking through use of flock
+instead of sysv semaphores.
+
+
+Version 4.1.0 - 8th March 2014
+
+- Correct fix for dev start time being adjusted for stat zeroing.
+- Make per device stats work for average after a stat zeroing.
+- Add an hfa-options command line that allows the clockspeed to be chosen per
+device by name comma separated, with a function that can be expanded with more
+options in the future.
+- Off by one drv_rolllimit check against jobs
+- Free the work that may be lost, leaking memory, in a failed hfa_send_frame
+- Roll the ntime for work within the hfa driver for firmware we know doesn't do
+it internally as an optimisation.
+- Export the roll_work function to be usable by driver code and make it
+compatible with rolling stratum work.
+- Make opt_queue be respected as a maximum value for staged items.
+- Disable mistakenly enabled lock tracking.
+- api version update for HEX32
+- api.c - HEX32 type needs quotes
+- Disable the MAX_CLOCK_DIFF check for newer hashfast firmwares since it's not
+required.
+- Store the hardware and firmware revision in the info struct for easy use in
+the hfa driver.
+- Only decrease the hfa clock rate if the device has been running for less than
+an hour before dying.
+- Change lack of op name response message in hfa driver
+- Check for lost devices at every write/read in hfa_detect_common
+- Make bxm bits configurable.
+- Move avalon2 options to ~alphabetic position in help.
+- Do a shutdown routine on bxm close.
+- Provide support for 2 chips in libbitfury sendhashdata and enable the 2nd chip
+on BXM devices.
+- Remove unnecessary opayload and newbuf members of bitfury info struct.
+- Add an spi add fasync command.
+- Cope with older hfa firmware not even responding to op_name.
+- Forcibly kill everything silently with an exit code of 1 should we fail to
+cleanly shut down and use a completion timeout for the __kill_work in
+app_restart.
+- Make __kill_work itself also be a completion timeout.
+- Generalise more of libbitfury for more reuse in both nf1 and bxm drivers.
+- Remove redundant init components of bxm driver.
+- Set default osc6 bits on bxm to 50
+- Enable the transaction translator emulator for bxm devices and use a dummy spi
+tx the size of a normal payload.
+- Store usb11 and tt flags as booleans in cgusbdev allowing them to be
+discretely enabled as well as detected by the device data.
+- Add bxm scan function and check spi txrx returns only as much as sent.
+- Add init sequence to bxm detect one.
+- Add a bxm specific txrx function for spi transfers.
+- Add bxm close to bitfury shutdown switch.
+- Add reset/purge/cshigh/low sequence to bxm init
+- Add bitmode init to bxm open sequence.
+- Add initial bxm opening sequence for detect one.
+- Add identifiers for bxm bitfury devices.
+- Clean up parse_method
+- More gracefully break out of parse_notify on a corrupted hex string error,
+checking the return value of all hex2bin conversions and being consistent with
+using stack memory. Fix an unlocking error in cases of failure.
+- AntS1 - add detection information to usbutils
+- Enable Bitmain Ant S1 code and make it conform to cgminer requirements
+- Make the cointerra displayed hashrate based on valid share generation.
+- Convert and update values shown in the cointerra api output.
+- Export the api_add_int16 function.
+- Use a custom mystrstr function in cointerra driver.
+- Add api_add_int16 to API functions.
+- Add support for Bitmain Multi Chain and Single Chain and optimize the
+efficiency
+- Add support for bitmain devices
+- Perfect function of BitMain Multi Chain
+- Add support for Bitmain Multi Chain and Single Chain and optimize the
+efficiency
+- Add support for bitmain devices
+
+
+Version 4.0.1 - 28th February 2014
+
+- Refresh the log window on pool failure message at startup.
+- Rework the pool fail to connect at startup to not get stuck indefinitely
+repeatedly probing pools with new threads and to exit immediately when any key
+is pressed.
+- Use an early_quit function for shutting down when we have not successfully
+initialised that does not try to clean up.
+- Add more information to a hfa bad sequence tail event.
+- Increase the work queue at the top end if we've hit the bottom as well.
+- Set the work generation thread high priority, not the miner threads.
+- Bringing each hfa device online takes a lot of work generation so only ever do
+one at a time.
+- Increase the opt_queue if we can hit the maximum amount asked for but are
+still bottoming out.
+- Keep the old hfa device data intact with a clean thread shutdown to allow it
+to be re-hotplugged with the old information.
+- Cope with the API calling hfa on partially initialised devices having no info.
+- Show only as many digits as are required to display the number of devices.
+- Cold plug only one hashfast device to get started, and then hotplug many to
+minimise startup delays and possible communication delays causing failed first
+starts.
+- Send a shutdown and do a usb_nodev if hfa_reset fails.
+- Null a device driver should thread prepare fail.
+- Add a function for making all driver functions noops.
+- Don't try to reinit a device that's disabled.
+- Disable a device that fails to prepare.
+- Check for lack of thread in watchdog thread for a failed startup.
+- Make all device_data dereferences in the hfa driver safe by not accessing it
+in statline before when it's non-existent.
+- Add an option to disable dynamic core shedding on hashfast devices.
+- Do not remove the info struct on a failure to hfa prepare.
+- Detect an hfa device purely on the basis of getting a valid header response to
+an OP_NAME query, leaving init to hfa_prepare which will allow multiple devices
+to start without holding each other up at startup.
+- Store the presence and validity of opname in the hfa info.
+- api - buffer size off by 1 for joined commands
+- minion - clean up statline
+- Only break out of usb_detect_one when a new device is found.
+- Use usb_detect_one in the hfa driver.
+- Provide a usb_detect_one wrapper which only plugs one device at a time,
+breaking out otherwise.
+- Issue a usb_nodev on a bad work sequence tail in hfa
+- Read in hfa stream until we get a HF_PREAMBLE
+- Add shed count to hfa API stats output.
+- Display the base clockrate for hfa devices with a different name to per die
+clockrates to be able to easily distinguish them.
+- Use op_name if possible first with hfa devices to detect old instances and be
+able to choose the starting clockspeed before sending an init sequence,
+reverting to setting op name and serial number as fallbacks.
+- Make hfa resets properly inherit across a shutdown.
+- Don't break out of hfa_old_device early if there's no serial number.
+- Fix harmless warning.
+- Allow the drop in MHz per hfa failure to be specified on the command line.
+- Icarus - ignore HW errors in hash rate ... and fix detection of them
+- Enable the hfa shed supported feature by default.
+- Add to udev rules hfa devices for firmware writing.
+- Remove ENV from hashfast udev rules.
+- Add a --hfa-name command that allows one to specify the unique opname for a
+hashfast device.
+- Ava2 decode the voltage, get the temp_max
+- Set the clock rate with a work restart instead of an init when changing to old
+clocks for hfa
+- Set opname on hfa devices without a serial number to a hex value based on time
+to not overflow the field.
+- Add op name to hfa API stats output if it exists.
+- Set the actual op_name in hfa devices if cgminer is choosing it itself due to
+it being invalid.
+- Re-init an hfa device to its old data before setting up info structures as
+their sizes may change.
+- Remove the usb device whenever we do a running shutdown on hfa and do a
+shutdown as the imitated reinit  to allow it to hotplug again.
+- Reset opt hfa dfu boot after it's used.
+- Comment out windows only transfer on hfa startup.
+- Clean up structures unused in case of all failures in hfa detect common
+- Clear all structures should we fail to hfa reset on adjusting clock on a
+hotplug.
+- Set master and copy cgpu hash clock rate for hfa when dropping it on a
+restart.
+- Set the master hfa clock speed to lower when shutting down a copy.
+- Do a clear readbuf on any hfa reset in case the device  has not yet cleanly
+shut down.
+- Increase hfa fanspeed slightly more when it's rising in the optimal range than
+falling.
+- Always decrease hfa clock speed on a running shutdown and don't try sending an
+init frame since it will be dropped regardless.
+- Match hfa devices to old ones based on OP_NAME values before serial numbers if
+possible.
+- Read off the OP_NAME if it exists and is supported on hfa devices, setting it
+to the device serial number or a timestamp if it is invalid.
+- Updated hf protocol
+- Check for an amount along with no error in hfa clear readbuf
+- Hfa clear readbuf can return a nonsense amount when there's a return error so
+ignore the amount.
+- Running resets always cause a shutdown on hfa meaning the device will
+disappear with modern firmware so always kill off the threads to allow
+re-hotplugging.
+- Reset the hfa hash clock rate to the old one if we find an old instance, only
+setting the device id in hfa_prepare
+- Keep the device_id on the original zombie thread for HFA in case of further
+resets.
+- Break out of hfa inherit if there is no device data.
+- Inherit the hfa zombie instance after the device id has been allocated.
+- The list_for_each_cgpu macro will dereference when there are no mining threads
+yet.
+- Make hfa hotplug inherit some parameters from a previous instance if the
+serial number exists and is matching, avoiding dropping the clock on all
+devices.
+- Per device last getwork won't work if the device stops asking for work.
+- Use the share_work_tdiff function in the driver watchdogs.
+- Provide a helper function for determining time between valid share and getwork
+per device.
+- Store last_getwork time on a per-device basis.
+- Limit the decrease of hfa clock rate on reset to the default clockrate.
+- Base the hfa failure time on the current expected hashrate instead of a static
+15 seconds.
+- We shouldn't be trying to read from the hfa_send_shutdown function itself.
+- Reset the icarus failing flag only when a valid nonce is found.
+- Transferred value is corrupt on a NODEV error in usbutils.
+- Set each miner thread last valid work just before starting its hash loop in
+case there are delays at startup.
+- Only memcopy *transferred data in usbutils if we have received only success or
+a non-fatal error.
+- Increase to 25 nonce ranges on icarus fail detect.
+- Set icarus device fail time to be dependent on device speed to avoid falsely
+detecting failure on slower AMU devices.
+- Updated hf protocol header.
+- Updated BE hf protocol header.
+- Take into account shed cores on hfa devices when determining how many jobs to
+send.
+- Fix compilation error with two avalon types.
+- Fix missing A1 files from distribution.
+
+
+Version 4.0.0 - 21st February 2014
+
+- Check for error from setfloatval
+- Halfdelay cannot be larger than 255.
+- Allow any arbitrary frequency to be specified for ANU devices and try to find
+the nearest frequency when initialising it, reporting if the frequency is not
+exactly as requested.
+- Only show system libusb warning when appropriate during configure.
+- Merge branch 'avalon2' of https://github.com/xiangfu/cgminer into
+xiangfu-avalon2
+- Hfa cooling remains satisfactory down to a minimum fanspeed of 5%
+- Give a nodev error if we have already set nodev in hfa clear readbuf to avoid
+further usb comms attempts.
+- Fix missing include
+- Move bitmine options to alphabetic positioning.
+- bab - missed a few 'DEAD's in last commit
+- bab - use 'bad' instead of 'dead' as per the screen B:
+- bab - roll work if possible to reduce CPU
+- Update the per die hash clock data on a running reset on hfa devices.
+- Set the per die clock on hfa to the known starting base clock instead of our
+requested clock rate.
+- Hfa device failure can be detected within 15 seconds so we should try
+restarting it sooner to avoid tripping the device's own watchdog.
+- Check return result of hfa clear readbuf to minimise error messages on device
+failure.
+- Put MHz into cta statline description.
+- Send a work restart with every shutdown message to hfa devices to clear any
+work that might be stale on the next restart.
+- Store the hfa hash_clock rate and display it in the statline.
+- Store the maximum board temperature for hfa devices and take that into
+consideration when calculating the highest temperature as well as the dies.
+- A1: CoinCraft-Desk driver variant
+- Initial import of Bitmine.ch A1 SPI driver
+- klondike ensure stats type matches
+- avalon, bab, drillbit, klondike use more screen space rather than truncating
+info
+- Add hashfast fanspeed% to statline display.
+- Move driver statline padding to cgminer.c, expanding width of maximum
+displayable statistics and window width to add more info.
+- Prune old stratum shares that we've seen no response for over 2 minutes to
+avoid memory leaks for pools that don't respond about some shares.
+- Add warning if system libusb is being added.
+- Only run ./configure with autogen.sh if extra parameters are passed to it.
+- Updated cointerra features.
+- Add le16toh defines for platforms that may be missing it.
+- Remove modminer bitstreams from distribution and replace with a README saying
+what file needs to be added if modminer build is still desired.
+- Use the simplelog function from usb_list()
+- Add a simplelog function that does not log date and time.
+- Use a unique usb_list function displaying only pertinent information when
+listing usb devices from the menu.
+- Abstract out the _in_use function to take different linked lists.
+- Break out of linked list loop in remove_in_use in case we've gone over the
+whole list.
+- Check for hfa invalid hash clockrate after other error messages.
+- Detect non-responsive icarus devices and attempt a usb reset before killing
+them after 2 minutes of no hashes.
+- Detect non-responsive bitfury devices and try a usb reset on them before
+killing their instances off after 2 minutes of no activity.
+- Allow hotplug interval to be changed from the USB menu.
+- Prevent recursive loop in __is_in_use linked list walking.
+- Add the ability to whitelist previously blacklisted usb devices from the menu.
+- Use a bool in struct cgpu to know when a usb device has been blacklisted,
+avoiding blacklisting it more than once.
+- bab - ensure disabled chips are counted in the screen dead chip counter
+- bab - only disable the chip once ...
+- bab - short work list skip disabled chips
+- api.c avoid incorrect gcc warning
+- cgminer -h crash fix
+- Add blacklisting as an option to the USB menu.
+- Add a mechanism to blacklist a usb device from its cgpu.
+- Add an option to the USB menu to list all known devices.
+- Add an option to send a USB reset via the USB menu.
+- Add a usb_reset by cgpu function to usbutils.
+- Add warning for attempting to unplug a usb device that is already removed.
+- Add USB Unplug option to USB management device management menu.
+- Add enable and disable USB device functions to the menu.
+- Add a [U]SB menu item, initially with just statistics per device, adding
+device number to the device status window display.
+- Reuse the cgpu temp entry for avalon and bitfury devices, changing avalon to a
+damped value.
+- Store the cointerra maximum temperature in the cgpu struct as an exponentially
+changing value based on the maximum temperature.
+- Reuse the cgpu->temp entry for max temperature in hfa driver.
+- bab - disable chips that return only bad results
+- Add driver for cointerra devices.
+- Add Avalon2 (2U size machine) support
+- miner.php - define a default rigport (that can be changed) and don't require a
+port number in the rigs array
+- miner.php allow links for rig buttons in tables and allow using the 4th IP
+octet if no rig name - default disabled for both
+- format fix and bad variable usage fix for --benchfile
+- Allow running cgminer in benchmark mode with a work file --benchfile
+- ANU frequency is in MHz, not hex.
+- Remove bitfury devices from the usb list on shutdown in case they have stopped
+responding but have not had a fatal usb error.
+
+
+Version 3.12.3 - 8th February 2014
+
+- Put the hashfast temperature into the cgpu structure so that it shows up in
+the devs API call.
+- We shouldn't block on no work situations directly from the getwork scheduler
+itself.
+- Revert "Make the pthread cond wait in the getwork scheduler a timed wait in
+case we miss a wakeup."
+
+
+Version 3.12.2 - 8th February 2014
+
+- Adjust antminer U1 timing according to command line frequency set, fixing the
+need for icarus timing on the command line.
+- Read pipe errors that don't clear are worth attempting to reset the usb.
+- Revert "Do away with usb resets entirely since we retry on both pipe and io
+errors now and they're of dubious value."
+- Make the pthread cond wait in the getwork scheduler a timed wait in case we
+miss a wakeup.
+
+
+Version 3.12.1 - 7th February 2014
+
+- Document new features for antminer U1 and hfa devices.
+- Add support for ANU overclocking.
+- Increase hfa fanspeed by more if we're rising in temp above the target than if
+the temp is staying the same.
+- Add debug output when get_work() is blocked for an extended period and add
+grace time to the device's last valid work to prevent false positives for device
+failure.
+- Issue a shutdown prior to a reset command for hfa devices and lock access to
+reads awaiting the response if the device is already running.
+- Do not register as successful a hfa init sequence that reports the clockrate
+as zero.
+- Show device info in noffset nonce share above target message.
+- Widen lines in top menu to fit extra large share values.
+- Only show one decimal place if pool diff is not an integer.
+- Show serial number as a hex value in hfa verbose startup.
+- Slowly remove work even if it's not being used to keep the getwork counter
+incrementing even if work is not used and as a test that pools are still
+working.
+- Increase the maximum diff between hfa dies to 100Mhz.
+- Show which hfa die is bringing down all the others when decreasing all the
+clock speeds.
+- Increase the decrease when temp has increased more and we want to decrease it
+on hfa.
+- Give device info with share above target message.
+- Allow throttling of hfa dies more frequently and increasing of speeds less
+frequently.
+- Wait after sending a hfa shutdown to allow the device to properly shut down
+before possibly sending it more commands.
+- Minimise the die clock differences in hfa to no more than 50Mhz.
+- Check for when errno is set on windows as well as the windows variant for
+errors.
+- Revert "Update to libusb-1.0.18"
+- Disable fan/die clock control in hfa if the firmware does not support it, with
+notification.
+- Add ability to enter ANU frequency as a multiple of 25 from 150-500.
+- Decrease hfa clock by 10 if a reset is attempted due to the device remaining
+idle.
+- ifdef out icarus options unused without icarus built in.
+- Reorder command line options alphabetically.
+- Add no matching work to hfa API output.
+- Change various logging message levels in the hfa driver.
+- Only adjust clocks if there is no restart in hfa to avoid 2 restarts back to
+back.
+- Ensure we iterate over all dies adjusting temperate for hfa by starting
+iterating after the last die modified.
+- Clamp initial hfa fanspeed to min/max if passed as parameters.
+- Allow hfa fanspeed to be set via command line.
+- Further relax the target temperatures on hfa driver, targetting 88 degrees.
+- Try one more time to get the hfa header on init since it can take 2 seconds
+for all 3 boards on a sierra.
+- Update authors for removal of gpu/scrypt.
+- Wait for 5 temperature updates in hfa before adjusting fanspeed.
+- Have some leeway before starting to throttle hfa dies.
+- Use increments of 10 when increasing hfa clock since it may not have 5 MHz
+granularity internally.
+- Only perform a hfa fan speed update if we have new temps to work with.
+- Correctly measure the hfa max temp and smooth out the changes in its value.
+- Choose better defaults for min/max/default fan settings for hfa driver.
+- bab - reduce def speed, fix speed staying in ranges and report bank/chips in
+ioctl() errors
+- bab - add info about number of boards/chips to each Dead Chain
+- These may not be longs (eg: OSX)... fo a safe cast to ensure.
+- bab - add dead boards and dead chains to stats
+- Add fanspeed to hfa api output and set initial fanspeed to 10%
+- Add hfa fanspeed control to try and maintain a target temperature.
+- API-README correct new text format documentation
+- API allow multiple commands/replies in one request
+- Add op commands necessary to control hfa fanspeeds.
+- Add OP_FAN to hf protocol header.
+- Always show the stratum share lag time in debug mode.
+- Add stratum share response lag time to verbose output if it's greater than 1
+second.
+- Add stratum share submission lag time to verbose information if it's over 1
+second.
+- Check for more interrupted conditions in util.c and handle them gracefully.
+- Send a ping to hfa devices if nothing is sent for over 5 seconds.
+- Add OP_PING to hfa commands
+- Display the hfa serial number as a hexadecimal value.
+- Add the ability to display a hexadecimal 32 bit unsigned integer to the API.
+- Limit all hfa restarts for temperature control to no closer than 15 seconds
+apart.
+- Allow the hfa temp target to be disabled by setting it to zero.
+- Handle interruptions to various select calls in util.c
+- Add sanity check for silly overflows in hfa die temperature readings.
+- Add per-die throttling control for hfa driver based on each die's temperature,
+issuing a suitable reset to maintain the temperature below a configurable target
+temperature.
+- Update hf protocol
+- Do not memcpy in usbutils unless data was transferred.
+- Send a full allotment of jobs to the hfa device after a restart instead of
+reading the status.
+- Export the flush_queue function for use by drivers.
+- Remove wrong goto
+- Remove the unqueued work reference when we discard work from get queued as
+well.
+- Wake the global work scheduler when we remove a work item from the unqueued
+work pointer.
+- Discard work that is stale in the get_queued() function, returning NULL
+instead.
+- Add a call to a driver specific zero stats function when zero stats is called
+to allow each driver to reset its own stats as well if desired.
+
+
+Version 3.12.0 - 29th January 2014
+
+- Add support for AntminerU1 devices with the icarus driver.
+- Add antminer U1 to comment in udev rules.
+- Do away with usb resets entirely since we retry on both pipe and io errors now
+and they're of dubious value.
+- Retry on usb IO errors instead of faking success.
+- Check that we've cleared the pipe error after a clear request, not the err
+value which is unchanged.
+- Update to libusb-1.0.18
+- Change hfa overheat limit to 90 degrees.
+- Relax timeout in hf get header to 500ms to match the usb timeout.
+- Minion - check/clear interrupts for all chips
+- Set info work to null after it is freed in nf1 after a restart to prevent
+double free later.
+- The second_run bool in libbitfury should be per device. Microoptimise its and
+job_switched usage, removing the unused results array for NF1 devices.
+- Fix displayed diff when solo mining at >2^32 diff.
+- bab - stop stale work accumulating
+- bab - set the default SPI speed back to 96000
+
+
+Version 3.11.0 - 25th January 2014
+
+- Add hashfast documentation to ASIC README
+- Support the variable HFA naming throughout the driver notices.
+- Set the global hfa hash clock rate to equal the lowest if we are lowering it
+for a device reset since it may be re-hotplugged after failing reset.
+- Decrease the hfa clock rate if it is overclocked and we have had to try
+resetting it.
+- Put a sanity check on the measured temperature in the hfa driver for obviously
+wrong values.
+- Avoid calling applog from within hfa statline before to avoid a deadlock.
+- Add throttling control to hfa driver, configurable at command line, nominally
+set to 85 degrees.
+- Reset hfa device if no valid hashes are seen for 1 minute from the last work.
+- Store when the last getwork was retrieved and display it in the API summary.
+- bab - also report dead chip count screen
+- Count share based hashes in the hfa driver with the device diff to get more
+frequent updates.
+- Only count 2/3 of the accumulated hashes on each pass through the hfa scan
+work loop to smooth out displayed hashrate.
+- bab add total history HW% to API stats
+- Test valid nonces in the hashfast driver allowing us to check against the
+target when trying to submit them.
+- No point casting a double to a uint64
+- Convert the hfa hashmeter to one based on successful share return and display
+the raw and calculated hash totals in the API.
+- bab - remove libbitfury dependency since it requires USB
+- Add description to hfa hash clock command.
+- Add hfa board temperatures to API output.
+- Wait for up to 0.5 seconds in the hashfast scanwork loop if no jobs are
+required.
+- Label HFA devices as B or S when their configuration matches babyjet or
+sierra.
+- Fix libbitfury being compiled in always by mistake.
+- bab - spelling
+- Add bab-options
+- bab - tune the chip speed based on error rates
+- bab record/report spie and miso errors
+- Win32 falsely comes up as big endian pulling in the wrong hf protocol header.
+- Remove unused components in hashfast driver.
+- Check in all usb communication places for hashfast driver that the device
+still exists.
+- Do not send a usb reset on a usb read pipe error.
+- Don't replace usb pipe errors with the pipe reset return code.
+- Updated hf protocol header.
+- The search for extra nonce is not worth performing in the hashfast driver.
+- Add core address to hfa parse nonce debugging.
+- Retry sending a frame once if it has failed in hfa_send_frame
+- Add extra hfa usb init errors.
+- Quiet now unused variable warning in hfa detect.
+- Remove unused variable.
+- Add board temperature to hfa debug
+- Make submit_tested_work return a bool about whether it meets the work target
+or not.
+- Provide a helper function for determining dev runtime and use it in the
+hashmeters used.
+- Look for hfa usb init header for 2 seconds, then resend the init twice more
+before failing.
+- Really only set up the hfa crc table once.
+- Generically increase the queue if we are mining on a pool without local work
+generation each time we run out of work.
+- Change new block detection message since longpoll is rarely relevant today.
+- Change the default clockspeed bits on nanofury devices to 50 and add a command
+line option to allow it to be changed.
+- Use unused line at the top of the log window which often gets stuck
+unchanging.
+- Clear pool work on a stratum reconnect message.
+- bab record/report spie and miso errors
+- bab - cleanup old work for dead chips also
+- bab add avg fail tests to API stats
+- bab report bank/board/chip for dead and v.slow chips
+- bab process all nonce replies per chip together
+- bab reduce work delays
+- bab record the number of E0s discarded
+- bab - modified result parsing
+- bab restore removed unused flag
+- configure - correct minion name
+- bab only scan valid nonce offsets
+- bab record continuous (and max) bad nonces
+- bab display Banks/Boards/Chips in the device window
+- Modify thread naming to make them easier to identify
+- bab reduce the work send delay
+- bab remove results polling
+- bab report SPI wait in seconds
+- bab report missing chips at start and API
+- bab ensure there's enough space for the nonce reply
+- bab correct stats 'Send Max'
+- bab allow long enough wait on ioctl() per board
+- bab more I/O stats
+- api.c 2014
+- api allow any size stats data
+- bab add processed links which excludes expired links skipped
+- bab report chips per bank, hw% and ghs per chip
+- bab lock access to new_nonces to ensure correct reporting
+- bab report V2 banks/boards during initialisation
+- bab expire chip work
+- bab use only k_lists and make work handling more refined
+- klist - allow adding to tail
+- bab remove old unused #define
+- bab correct for master git
+- correct klist reallocs
+- klist lists for bab
+- api.c correct DEVICECODE and ordering
+- Maxchips should be 384 (16 chips/board 24 boards/controller)
+- bab more detailed stats and delay less when waiting for a buffer
+- api add data type AVG float 3 decimal
+- bab - add V2 detect with bug fix in detect
+- api.c set the actual version number to 3.0
+- API V3.0 unlimited socket reply size
+- README update --usb
+- Check for loss of device in usb read before any other code on the usbdev
+- Change stratum strings under stratum_lock in reconnect and free old strings.
+- Add mcp2210 compilation to want_libbitfury configs.
+- Fix HF driver typo.
+
+
+Version 3.10.0 - 9th January 2014
+
+- Set the mcp2210 transfer setting only when it changes.
+- Buffer sizes in nanofury device data are unnecessarily large.
+- Only perform spi reset on init, not with each transaction.
+- Remove spi_detect_bitfury at nanofury startup and fix incorrect refresh time.
+- Use a simple serialised work model for nanofury
+- Use bitfury_checkresults to avoid hashing results twice in nanofury.
+- Export bitfury_checkresults in libbitfury
+- Pass extra parameters for later use in libbitfury_sendHashData
+- Avoid double handling bswap of the nonce value in nanofury
+- Avoid unnecessary rehashing in nanofury nonce checking.
+- Remove the unused portions of atrvec in the nanofury driver
+- Age work in nf1_scan to avoid risk of losing a work item and leaking memory.
+- bitfury_work_to_payload is double handling the data unnecessarily
+- Default bitrate on nanofury should be 200kHz
+- localvec should be only 80 bytes not 80 words
+- Wrong init value for nanofury
+- Remove unused rehash values from nanofury driver.
+- Only update info work in nanofury driver when it's empty.
+- Fill the appropriate type of usb transfer when we know if it's an interrupt
+transfer instead of a bulk one.
+- Use the internal knowledge of the usb epinfo to determine whether we should be
+doing an interrupt instead of a bulk transfer, and do not send a ZLP if so, and
+limit read transfer to expected size automatically.
+- Avoid bin2hex memleak when we start getting nanofury nonces
+- Set atrvec only once and use a local array for each device's work.
+- Cancel any spi transfers on nf1 close
+- Add bitfury detection loop to nanofury startup
+- Move spi init code to libbitfury
+- Remove inappropriate extra config reg in nanofury setup.
+- Status 0x30 should never happen with spi transfers.
+- Fix spi transfer data size transmission mistakes.
+- Minor correctness change in spi_add_data
+- spi_txrx should always send and receive the same size message
+- Random libbitfury changes.
+- Set value of gpio pins to low on closing nanofury.
+- Fix more init sequence for nanofury.
+- Add basic initialisation for nf1 devices
+- Add basic nf1_scan function.
+- Basic import of libbitfury functions from nanofury branch
+- Import functions from nanofury fork for libbitfury
+- Meter out spi sends to only 2 bytes at a time, offsetting according to how
+much data returns.
+- Use the usb read limit function for mcp2210 reads.
+- Provide a way for usb reads to just read the size asked for with a limit bool.
+- Get pin value after an nf1 spi reset.
+- Make sure what we send in the buffer doesn't change during spi reset for
+nanofury
+- Remove all standalone gpio setting change functions in mcp2210 and just use
+the one global setting function.
+- Set gpio values in the one function with all values for nanofury.
+- Provide a helper function for setting all mcp2210 gpio settings.
+- Add a helper function for getting all mcp2210 gpio settings.
+- Set all pin designations and directions in one call for nanofury and don't
+bother storing their values in the info struct.
+- Provide helper functions for setting all pins and dirs on mcp2210
+- Set all nanofury pin designations in one call
+- Provide a helper function for setting all pin designations on mcp2210
+- Store the spi settings in a struct for nanofury devices.
+- Check the received status in mcp2210 spi transfers and repeat a zero byte send
+if it's in progress.
+- Set the bytes per spi transfer prior to each mcp2210 transfer.
+- Separate out the send and receive functions for mcp2210 and check response
+value in return.
+- Check that mcp2210 spi settings have taken and check the value of the pin
+during nanofury setup.
+- Don't set GPIO pin designations after initial setting in nanofury since the
+direction and values will be changed.
+- Provide an mcp 2210 set gpio input helper function that sets a pin to gpio and
+input.
+- Move the set gpio output function to a generic mcp2210 version from nanofury
+which also sets the pin to gpio.
+- Implement a nanofury txrx with a larger buffer and cycling over data too large
+to send.
+- Implement magic spi reset sequence for nanofury.
+- Add more spi magic to the nanofury init sequence.
+- Add lots of magic spi initialisation to nanofury.
+- Export reused components of bitfury management into a libbitfury and use for
+bab and bitfury drivers.
+- More init sequence for nanofury and implement a close function that sets all
+pins to input.
+- Reword offset header handling in hfa_get_header
+- Sanity check in hfa_get_header
+- Add more checks in hashfast driver for lost devices.
+- Change spimode and send more data in nanofury setup.
+- Add basic setup  comms to nanofury.
+- Implement an mcp2210 spi transfer function.
+- Set the initial spi settings for nanofury driver.
+- Provide a helper function for gettings mcp2210 spi settings.
+- Implement an mcp2210 set spi transfer settings function.
+- Cancel any SPI transfers in progress in nanofury after initial setup.
+- Implement an mcp2210 spi cancel function.
+- Return only binary values for mcp2210 GPIO values.
+- Set GPIO LED and power to high in nanofury driver.
+- Implement initial part of nanofury init sequence for GPIO pin settings and add
+output debugging of set values.
+- Add helper functions for getting and setting mcp2210 gpio pin designations.
+- Don't return an error in usb read if we've managed to get the whole read
+length we've asked for.
+- Use correct endpoint order for nanofury devices and read with a short timeout
+on return loop from send_recv.
+- Add mcp2210 helper functions for getting and setting one GPIO pin val and
+direction.
+- Create a generic gpio pin struct and add helpers for mcp get pin val and dirs.
+- Check the receive msg of a send/receive cycle on mcp2210 matches the send
+message.
+- Add a set of usb commands to the usbutils defines for mcp2210 comms, and use
+the same command name for send and receive.
+- Create a generic mcp2210 send_rcv function.
+- Include mcp header for bitfury and fix extra params in macro.
+- Add basic SPI comms defines for mcp2210 and build rules for bitfury.
+- Minion set some core defaults similar to final requirements
+- minion compile warnings
+- move driver-minion.c to main directory
+- Minion with ioctl() stats, settings to attempt to emulate 21TH/s
+- minion driver with results interrupt working
+- tested working driver-minion.c without interrupts
+- Working driver-minion.c v0.1
+- driver-minion.c compilable untested
+- minion driver - incomplete
+- Add minion driver into cgminer
+- Add basic device detection and updated udev rules for nanofury devices.
+- Remove GPU from share logging example.
+- Don't keep resetting BXF clockspeed to default.
+- If no pools are active on startup wait 60s before trying to reconnect since we
+likely have the wrong credentials rather than all the pools being out.
+- Discard bad crc packets for hashfast driver instead of trying to process them.
+- Update documentation for modified avalon options syntax and document relevant
+55nm details.
+- Modify the auto tuning sequence to work with the 50MHz changes required to
+work with 55nm Avalon.
+- 55nm avalon requires the delays between writes reinstated for stability.
+- Use an equation instead of a lookup table to set the frequency for 55nm avalon
+allowing arbitrary values to be used.
+- Make the result return rate low detection on avalon less trigger happy.
+- Always send the bxf device a clockspeed after parsing the temperature in case
+the device has changed the clockspeed itself without notification.
+- Fix BXF being inappropriately dependent on drillbit.
+
+
+Version 3.9.0 - 23rd December 2013
+
+- drillbit asic - enable in api.c
+- Fix trivial warnings in knc driver.
+- Reinstate work utility based hashmeter for knc.
+- drillbit format %z not valid on windows
+- drillbit more formatting changes
+- usbutils remove old code added back
+- Memset the spi tx buffer under lock in knc driver.
+- drillbit fix temp display to fit in standard space
+- Drillbit formatting
+- drillbit - use one drvlog and display dname before add_cgpu
+- Keep orginal naming for the bitfury driver
+- knc: Bugfix - good shares wrongly reported as HW errors.   Root cause of the
+problem: several work items were assigned the same   work_id in the active works
+queue of the knc driver. Thus when good   nonce report arrived from the FPGA,
+wrong work item was picked up from   the queue, and submit_nonce evaluated that
+as an error.   Fix: Limit the work_id counter update rate. Update it only to the
+number of   works actually consumed by the FPGA, not to the number of works
+send.
+- Store per-chip submit information for bxf device and show them in the API.
+- Check for removed bxf devices before trying to update work or send messages.
+- api.c no decref if not json
+- Minimise risk of nonce2 overflow with small nonce2 lengths by always encoding
+the work little endian, and increasing the maximum size of nonce2 to 8 bytes.
+- Change default hashfast timeout to 500ms.
+- Ensure we can look up the work item in the hashfast driver or print out an
+error if we don't.
+- Drillbit source formatting - reindent and retabify
+- Add ASIC count, temperature status to drillbit API output (closes #1)
+- Many warning fixes
+- knc: Do not include variable "last minute" data into the "last hour" per-core
+stats
+- knc: Make per-core statistics available through API
+- Implement command line control of the bxf target temperature.
+- Add a simple PID-like controller to bi*fury devices to dynamically alter the
+clock setting to maintain a nominal target temperature set to 82 degrees.
+- Add data to BXF API output.
+- Add support for newer protocol bi*fury commands job, clock and hwerror,
+setting clock to default 54 value, turning parsing into a compact macro.
+- Look for the thermal overload flag in the gwq status message in the hashfast
+driver and send it a shutdown followed by an attempted reset.
+- Log message fixups
+- Fix for "Timing out unresponsive ASIC" for pools which send early reconnect
+requests, and then take a short time to send work (ie BTCGuild)
+- Shorten initial config line, win32/pdcurses doesn't like long lines during
+early logging
+- Pull back the very long timeouts set in fe478953cf50
+- Fix bug where work restart during results scan could lead to bad device state
+- Align device status lines same regardless of number of temp status or >10
+ASICs
+- Tag log lines from brand new devices as DRB-1 until they are initialised
+- Tag log lines as 'DRB0' rather than 'DRB 0', same as other places in cgminer
+- Print a summary of the device settings at level NOTICE during initialisation
+- Allow chosing device settings based on 'short' product names shown in status
+line
+- Allow per-device settings to use "DRBnn" as an identifier instead
+- Issue an ASIC restart during a work_restart, removes spurious timeout messages
+from ASICs and probably some rejected shares
+- Check all results against all work instead of just taking the first match
+(avoids some rejected submissions to the pool, ASIC can produce multiple
+candidate results.)
+- Fix memory leak caused by unnecesarily copied work
+- Fix bug with find_settings not returning default value
+- Set timeouts on write, set very long timeouts
+- Merge drillbit driver
+
+
+Version 3.8.5 - 10th December 2013
+
+- Increase the BFLSC overtemp to 75 for fanspeed to maximum.
+- Set bflsc cutoff temperature to 85 degrees and throttle 3 degrees below the
+cutoff temperature.
+- Only set LIBUSB_TRANSFER_ADD_ZERO_PACKET for libusb versions we know include
+support for.
+- Provide a helper function that can reset cgsems to zero.
+- Add to cgminer_CPPFLAGS instead of redefining them.
+- Attempt a libusb reset device on usb devices that have stopped responding.
+- Replace deprecated use of INCLUDES with _CPPFLAGS.
+- Remove more unused GPU code.
+- Attempt USB device resets on usb read/write errors that will normally cause
+the device to drop out.
+- Quieten down jansson component of build.
+- Cache the bool value for usb1.1 in _usb_write
+- Initialise usb locks within usbutils.c instead of exporting them.
+- Imitate a transaction translator for all usb1.1 device writes to compensate
+for variable quality hubs and operating system support.
+- Rationalise variables passed to usb_bulk_transfer.
+- Unlink files opened as semaphores on releasing them.
+- Remove user configuration flag from pll bypass enabling in hashfast driver.
+- Provide an hfa-dfu-boot option for resetting hashfast devices for
+reprogramming.
+- Fixed one byte stack overflow in mcast recvfrom.
+- Having changed C_MAX means we don't calloc enough for usb stats, off by one.
+- Don't free the info struct on hashfast shutdown since it's still accessed
+after a device is removed.
+
+
+Version 3.8.4 - 1st December 2013
+
+- Deprecate the usb usecps function and just split up transfers equal to the
+maxpacketsize on usb1.1 devices.
+- Retry sending after successfully clearing a pipe error.
+- Drop logging of timeout overrun message to verbose level.
+- Use a much longer callback timeout for USB writes on windows only as a last
+resort since cancellations work so poorly.
+- Use vcc2 in bflsc voltage displayed.
+- Increment per core errors on false nonces in bflsc and add per core statistics
+to api stats, removing debugging.
+- Store a per-core nonce and hw error count for bflsc.
+- Fix json parsing in api.c
+- Add debugging to hfa driver for how many jobs are being sent.
+- Shut down the hfa read thread if the device disappears.
+- Add debug output saying what frame command is being sent in hfa driver.
+- Revert "Disable USB stats which were not meant to be enabled by default and
+add extra memory for a memory error when stats are enabled."
+- Reset work restart flag in hfa driver since we may check for it again in
+restart_wait.
+- Add more op usb init errors for hfa driver.
+- Perform basic displaying of hfa notices received.
+- Add hfa op usb notice macros.
+- Update hf protocol header.
+- Use sync usb transfers in lowmem mode.
+- Go back to allowing timeout errors on USB writes to be passed back to the
+driver without removing the device in case the driver wishes to manage them.
+- Initialise more values for the hfa data structures.
+- A USB control error must be < 0
+- Simplify USB NODEV error checking to success only for writes and control
+transfers, and success and timeout for reads.
+- libusb error IO should be fatal as well if it gets through usb read and write.
+- Allow IO errors in usb reads/writes to be ignored up to retry max times.
+- Use correct padding for bxf temperature display.
+- Initialise devices before attempting to connect to pools to allow their thread
+prepare function to be called before having to connect to pools.
+- Add hidden hfa options to set hash clock, group ntime roll and pll bypass,
+fixing frame sent on reset to include extra data.
+- Relax the timeouts for the slower usb devices on linux.
+- Add big endian hf protocol header to Makefile
+- Check for correct big endian macro in hf_protocol
+- Use an absolute timeout in hfa_get_header to cope with buffered usb reads
+returning instantly confusing the 200ms counter.
+- Update hfa_detect_one to use the new detect function API.
+
+
+Version 3.8.3 - 23rd November 2013
+
+- Set the bitfury device start times from when we first get valid work.
+- Fix stack corruption of zeroing too much in bf1 driver.
+- Make usb_detect return the cgpu associated with it to check if it succeeds to
+decide on whether to increment the device count or not.
+- Set tv work start time for bxf driver.
+- Age the bxf work items over 90 seconds, not the bf1 work items.
+- Zero the read buffer in _usb_read to avoid stale data and only use stack
+memory instead of using the bulkbuf since it is only used in _usb_read.
+- Leave room for temperatures above 100 degrees and pad consistently for bxf
+statline.
+- Drop json stratum auth failed message log level to verbose.
+- Change the processed value not the bufsiz in response to an end of message
+marker.
+- Don't lose data beyond the end of message in a usb read.
+- Silence irrelevant warning.
+- Only check strlen on end if end exists.
+- Simplify the end of message detection in _usb_read and allow it to return
+without doing another read if the message is already in the buffer.
+- Increase work ageing time to 90 seconds for bxf driver to account for firmware
+changes.
+- Use the age_queued_work function in the bitfury driver.
+- Provide a function to discard queued work based on age.
+- The json_val in api.c is a borrowed reference, not a new one so don't decref
+it.
+- Decrement json references in api.c to not leak memory.
+- line 2913 added urlencode
+- With reliable writes to the avalon there is no need for the sleep delays
+between writes.
+- There is no need to limit usb write transfers to maxpacketsize and it's
+harmful for large transfers on slow devices such as wrt routers.
+- Disable USB stats which were not meant to be enabled by default and add extra
+memory for a memory error when stats are enabled.
+- Set limit and count to integers to not overflow during failed hotplug attempts
+and then not trying again.
+- Update api example compilation instructions.
+
+
+Version 3.8.2 - 16th November 2013
+
+- Add more verbose documentation to the readme files for windows users.
+- Add more information on libusb failure to init telling users to check README
+file.
+- Add information on unloading cdc drivers on osx to README
+- Prevent a deadlock with use of restart_threads by spawning a thread to send
+the driver flush work messages.
+- Set priority of various threads if possible.
+- Add bxf data to api output.
+- Do not hold the mining thread lock in restart_threads when calling the driver
+flush work commands.
+- Send extra work regularly to the bxf device and parse the needwork command by
+sending the amount of work it requests.
+- Allow messages to have arbitrary offsets in the bxf parser in case we have
+lingering buffered data.
+- Send the maxroll command to the bxf driver and store the value to see if we
+need to update it.
+- Add sending of flush command to bxf on flush_work
+- Add flush and version commands to bxf start up, flush buffer and try to parse
+version response string.
+- Abstract out bxf recv message.
+- Add extra bxf commands to usbutils
+- Abstract out bxf send message to allow us to easily add extra commands.
+- Don't run device restart code if the device is not enabled.
+- Expand size of bitfury statline
+- Various driver fixes for bitfury devices, including a flag from when first
+valid work appears.
+- Look up work results in bxf driver from correct variable.
+- Correct incorrect error code in bxf driver for usb writes and add debugging.
+- Add bxf details to usbutils.
+- Implement a statline showing temperature for bxf
+- Add api data for bxf device, sharing the hashrate function with bf1.
+- Count no matching work as a hw error on bxf
+- Add BXF to udev rules.
+- Work id should be hexadecimal in bxf messages.
+- Add unrecognised string debugging to bxf driver.
+- Implement the main scanloop for bxf, trying to prevent it from ntime rolling
+work if the work protocol does not allow it.
+- Parse bxf work submits fully, submitting the results.
+- Provide a function for setting the work ntime.
+- Implement a skeleton parse bxf submit function.
+- Use the bxf read thread to set the device target and send its first work item.
+- Implement a bxf send work function and set update and restart functions to
+sending new work since that's the equivalent for that device.
+- Add temperature parsing to bxf driver
+- Create and destroy a basic bxf read thread.
+- Remove the buffer from bitfury info since it is only used on one pass in the
+bf1 device.
+- Add a rudimentary bxf detect one function.
+- Rename all bf1 specific functions in the bitfury driver, using a switch to
+choose correct function.
+- Rename bitfury_getinfo to bf1_getinfo since it's unique to bf1 devices.
+- Separate out the bf1 reset from bitfury reset.
+- Store the bitfury identity in the info struct.
+- BaB - updated tested OS comment
+- Uniquely identify the BF1 and BXF bitfury devices.
+- Remove the default libusb WinUsb pipe policies that don't suit us.
+- Only set the winusb pipe policy if it doesn't match our requirements instead
+of every transfer.
+- klondike - dont try to flush if not initialised
+- api.c trylock() add missing locklock
+- Use our new zero length packet support directly in windows.
+- Enable support for zero length packet on windows and auto clear pipe stalls.
+- util.c: Decreasing reference count on allocated JSON obects to prevent memory
+leak
+- api.c: Release apisock on error in api()
+- api.c: Release io_data->ptr when releasing io_data in io_free()
+- We can't connect to a GBT pool at all with fix protocol enabled.
+- Initialise the stgd lock mutex earlier to prevent dereferences when pool
+testing occurs before it.
+- Klondike support I2C USB layout also - as KLI
+- Return error codes in avalon_read() if they're not timeouts.
+- Break out of the avalon idle loop if we get a send error.
+- Set avalon ftdi latency to just less than the time it would take to fill the
+ftdi buffer at 115200 baud
+- Update example.conf
+- Only limit packetsize on usb out writes.
+- We must chop up every 64 bytes returned on an ftdi chip, not just the first 2
+bytes so revert to parsing the data internally in the avalon instead of using
+usbutils' simple ftdi parser.
+- Only retry 3 times in hfa_reset.
+- Only add_cgpu in hashfast driver once we have a real driver set up.
+- Clean up properly if hfa_detect_common fails in the hashfast driver.
+- --shares should be scaled to diff1 not absolute number of shares
+
+
+Version 3.8.1 - 11th November 2013
+
+- Revert "Send a zero length packet at the end of every usb transfer on windows
+in case libusb internally has batched them into one maxpacket sized."
+
+
+Version 3.8.0 - 10th November 2013
+
+- api update version to 2.0 and remove GPU form API-README
+-Remove now unused scrypt files.
+- api.c remove all GPU/gpu references and correct code as required
+- Rudimentary removal of GPU OpenCL and Scrypt features from api.c
+- Reorder configure alphabetically for devices to compile and fail if no support
+is selected to be compiled in.
+- BaB update/format some comments
+- BlackArrowBitfury early GPIO V1 driver
+- Fine tune the reading of results in bitfury driver to not lose any across work
+restarts or corrupt due to store results not parsed during restart.
+- Send a zero length packet at the end of every usb transfer on windows in case
+libusb internally has batched them into one maxpacket sized.
+- Framework for ntime rolling, keep looking for OP_USB_INIT replies when other
+packets received
+- Configure source for a new BaB driver
+- sha2 allow external access to some macros and the K array
+- Fixed a math issue when reporting fan speed on the status line.
+- Use the main hashlist to store work done in the bitfury driver and remove work
+from the list by time, thereby fixing the duplicates at startup. Count hardware
+errors for when no match occurs.
+- Add a get and queue helper work function.
+- Remove GPU mining code.
+- Use libusb's own zero length packet support unless we have to emulate it on
+windows since only libusb knows for sure if it's needed.
+- Unlock the avalon qlock while sending tasks to not hold the lock for an
+extended period.
+- Sleep in avalon send task on return to the function to allow other code to
+work during the sleep period.
+- Send zero length packets when terminating a usb write aligned to
+maxpacketsize.
+- Do the driver flush in avalon code lockless since it can lead to deadlocks.
+- Reset the work_restart bool after the scanwork loop in case the driver flushes
+work synchronously.
+- Only check for the stratum clean message if we have had a valid message.
+- Get rid of the stage thread since all work can be asynchronously added now via
+hash_push anyway.
+- Remove the now incorrect faq entry regarding scrypt difficulty.
+- Check for fatal read errors and break out of the read loop in avalon.
+- Send errors are basically fatal in avalon driver so break out of the send
+tasks loop.
+- Make the avalon driver return -1 for hash count when usb fails, allowing the
+main loop code to send it the shutdown flag.
+- Break out of the hash work loops when a failure is detected instead of
+dropping into mt disable.
+- Use usbutils' own ftdi parser for avalon and the ftdir's own latency for
+managing timeouts since we can wait on reads with completely asynchronous
+reads+writes.
+- Use usbutils' own cps function for slowing rate of usb writes on avalon.
+- Fix build for no libcurl
+- Check length before submitting sync transfers
+
+
+Version 3.7.2 - 5th November 2013
+
+- Clean up completely on avalon shutdown.
+- Use cgsem timed waits in avalon driver to not miss any queued wake ups to
+account for async messages coming during a flush work.
+- Statline before is too long on icarus that doesn't have monitoring.
+- Different windows+usb combinations respond with varying levels of reliability
+wrt timeouts so use a nominal extra 40ms before cancelling transfers that fail
+to time out on their own.
+- Do all hotplug_process under the write mining_thr_lock
+- Fix for opt_worktime on big endian machines.
+- Correct set_blockdiff for big endian machines.
+- Make sure cgpu exists in the restart threads loop in cases of hotplug etc.
+- Treat usb write timeout errors as unrecoverable.
+- Transfer errors are filtered out in usbutils now so no need to look for them
+in NODEV checks.
+- Remove now unused entries from struct cg_usb_device
+- Do not double up with checking for end of timeout measurements in usb
+read/write.
+- Do get_work in fill_queue without holding other locks.
+- Initialise usb after all the locks and conditionals are initialised.
+- Use only a trylock in flush queue to prevent deadlocks.
+- Add a wr_trylock wrapper for pthread rw lock write trylock.
+- Scale diff for scrypt when testing for block solves.
+- Fix for non curses build.
+
+
+Version 3.7.0 - 4th November 2013
+
+- Use WRITEIOERR macro check for all usb writes.
+- Always use a usb read buffer instead of having to explicitly enable it.
+- Force unlocking of the console lock on restart to avoid corrupting the console
+state when we finally quit.
+- Never wait indefinitely for a pthread conditional in the hash_pop loop in case
+the work scheduler misses the last wakeup.
+- Make hash_pop signal the work scheduler each time it waits on the conditional
+that it should look for more work.
+- Discriminate between libusb transfer errors and regular libusb errors and make
+sure to capture them all.
+- Always read a full sized transfer for bulk reads.
+- Deprecate preferred packet size functions in usbutils since they're unhelpful.
+- Copy known transferred amount back to buffer for usb reads instead of
+requested length.
+- Treat timeout errors on usb writes as IO errors.
+- Ignore iManufacturer from bitfury devices to support bluefury as well as
+redfury.
+- Add more debugging info for when usb details don't match.
+- Look for timeout overruns in usb read/write.
+- Use an int for usb_read/write to identify overruns.
+- Use the callback timeout as a safety mechanism only on windows.
+- Instead of using complicated sleeps to emulate characters per second on usb
+writes, submit only as many characters as can be transferred per usb poll of
+1ms, and use timeouts in bulk transfers, cancelling transfers only as a
+failsafe.
+- Remove discarded work from quota used.
+- Display works completed in summary and API data.
+- Store how many work items are worked on per pool.
+- Make each pool store its on reference for what the most current block is and
+fine tune management of block change in shared pool failover strategies using
+the information.
+- Rationalise use of current_hash to a single hex string the length of the
+previous block and display only the first non zero hex chars of the block in the
+status window.
+- Update uthash to latest.
+- show_hash doesn't know the size of the string so hard code the max size.
+- Remove as many initial zeroes as exist on share display, abstracting out a
+hash show function to use across different submission mechanisms.
+- Add missing endian swap functions for 64bits.
+- Sanity check for absurd target setting and divide by zero.
+- Abstract out conversion of a 256 bit endian number to a double, correcting
+errors and use it for determining any magnitude share diff.
+- Avoid the extra generation of a byte flipped hash2 in struct work and directly
+use the LE work hash.
+- Add a sanity check to avoid divide by zero crashes in set_target
+- Calculate diff from target accurately for all 256 bits.
+- Set a true 256bit binary target based on any diff value in set_target()
+- Provide a copy_work_noffset function for copying a work struct but changing
+its ntime.
+- Make calls to flush queue and flush work asynchronous wrt to the main work
+loops.
+- Share is also above target for submit noffset nonce.
+- Use round for displaying current pool diff.
+- Use round for stratum share diff display instead of floor.
+- Use round instead of floor for displayed pool difficulty.
+- Allow arbitrary diffs to be tested against nonces via a test_nonce_diff
+function.
+- Abstract out the rebuilding of hash2 in work.
+- Share is above, not below target, when it doesn't meet it.
+- Add the ability to add uint8 and uint16 entities to api data.
+- Use a non blocking connect with a 1 second select timeout when initiating
+stratum to allow us to iterate over all IPs returned by getaddrinfo in round
+robin DNS pools.
+- Minor style changes to output.
+- Revert two different hash_sequence(_head)'s to one variable, use
+HF_SEQUENCE_DISTANCE in both places
+- Remove duplicate HF_SEQUENCE_DISTANCE() macro, and duplicate hash_sequence
+from info structure
+- Change SEQUENCE_DISTANCE() macro to HF_SEQUENCE_DISTANCE()
+- Structure changes for OP_NONCE, add big endian header
+- klondike - initialise stat_lock
+- klondike - better to unlock locks than to lock them twice :)
+- Add copyright notice to knc driver.
+- Trivial style changes to knc driver.
+- Improve performance of work generation by optimizing hex2bin and bin2hex
+- klondike - change options to clock and temptarget only
+- klondike - fix another uninit dev warning
+- klondike - downgrade 'late update' but add an idle detect - and correct error
+levels
+- klondike - fix isc uninit warning
+- Use a mutex to protect data in the knc structure, to prevent loading more work
+during a flush, and unlock and return to main between calls to get_queued_work.
+- Use the existing device_data for knc state data.
+- Only count successful nonces as hashrate in the knc driver.
+- Fix trivial warnings in knc driver.
+- Add KNC to api
+- klondike - drop the device for hotplug if it's unresponsive
+- usbutils - usb_nodev() allow a driver to drop a device
+- klondike - single 'shutdown' and ensure it happens
+- klondike remove SCNu8 - unsupported on windows
+- Correctly calculate sleep_estimate in usbutils that may have been preventing
+usecps from working.
+- Use a sanity check on timeout on windows.
+- Better HW error count; disable permanently those cores which fail often
+- KnC driver: knc-spi-fpga ASIC driver
+- Fixup jansson & libusb include paths when using separate build directory
+- 'llround' is more suitable here than 'roundl'
+- Silence warning if MAX/MIN is already defined
+- Remove prebuild ccan/opt dependencies
+- Reinstate block solve testing.
+- Dramatically simplify the calculation of blockdiff.
+- Simplify the set_target function, allowing it to work properly for fractional
+diffs.
+- Merge hashfast driver
+- Merge KnC driver
+
+
+Version 3.6.6 - 26th October 2013
+
+- Remove inappropriate extra locking in _usb_transfer_read
+
+
+Version 3.6.5 - 26th October 2013
+
+- klondike - fix uninitialised dev bug
+- Adjust the binary ntime data in submit_noffset_nonce even when there is no hex
+ntime string for eg. gbt.
+- Put an entry into the work struct telling drivers how much they can roll the
+ntime themselves.
+- Only set libusb cancellable status if the transfer succeeds.
+- Remove the applog on miner threads dying to prevent deadlocks on exit.
+- Do one extra guaranteed libusb event handling before testing if there are any
+pending async usb transfers.
+- Use a linked list for all usb transfers instead of just cancellable ones.
+- Provide a mechanism for informing drivers of updated work templates for
+stratum and gbt mining.
+- Add cancellable transfers correctly to the ct_list
+- Check for presence of thr in icarus get nonce for startup nonce testing to
+work.
+- Use cancellable usb transfers in the icarus driver to avoid having to loop and
+poll when waiting for a response and to speed up work restart response time.
+- Add a usb_read_ii_timeout_cancellable wrapper
+- Add usb transfer cancellation on shutdown and documentation regarding where
+cancellable transfers are suitable.
+- Use cancellable transfers on bitfury device.
+- Cancel cancellable usb transfers on work restart messages.
+- Don't bother having a separate cancellable transfer struct for usb transfers,
+simply include the list in the usb_transfer struct.
+- Add wrappers for usb_read_cancellable and usb_read_timeout_cancellable
+- Specifically set the cancellable state for it to not be uninitialised in the
+usb transfer struct.
+- Alter the usb cancellable list only under cgusb_fd_lock write lock.
+- Pass the cancellable option to _usb_read options to decide on whether to add
+usb transfers to the list of cancellable transfers.
+- Create a linked list of potentially cancellable usb transfers.
+- Don't attempt to disable curses or print a summary during an app restart to
+prevent deadlocks.
+- Keep the libusb event handle polling thread active until there are no async
+usb transfers in progress.
+- Keep a global counter of how many async usb transfers are in place.
+- Perform libusb_submit_transfer under the write variant of cgusb_fd_lock
+- klondike - error condition handling
+- Avoid entering static libusb directory if --with-system-libusb is enabled.
+- Minor opencl build corrections.
+- Enable dynamic linking against system libusb --with-system-libusb
+- Modify Makefile to only include opencl related code when configured in.
+- Convert opencl to need to be explicitly enabled during build with
+--enable-opencl
+- Implement a cglock_destroy function.
+- Implement a rwlock_destroy function.
+- Implement a mutex_destroy function.
+- Add usb command name to critical libusb error reporting.
+- Use windows' own higher resolution time and handlers allowing us to have
+higher precision absolute timeouts.
+- Fix lldiv error in windows cgminer_t calculation.
+- miner.php correct sort gen field names largest to smallest
+- api ... the code related to device elapsed
+- api add device elapsed since hotplug devices Elapsed is less than cgminer
+Elapsed
+- Drop usb buffering message to debug logging level.
+- Do the ntime binary modification to the work struct when submitting an ntime
+offset nonce within submit_noffset_nonce
+- Code cleanup and improved documentation
+- Improvements to support for BitBurner boards
+- Convert libusb transfer errors to regular libusb error messages to allow for
+accurate message reporting.
+
+
+Version 3.6.4 - 18th October 2013
+
+- Fixing the memory leak for remaining semaphores means we can go back to using
+async transfers on other OSes with our own timeout management again.
+- Use the forcelog function on shutdown to cope with indeterminate console lock
+states due to killing of threads.
+- Add a forcelog variant of applog which invalidates any console lock to force
+output.
+- Send pthread_cancel to failed completion_timeout that has timed out.
+- Simplify queued hashtable by storing unqueued work separately in a single
+pointer.
+- bflsc use getinfo chip parallelization if it is present
+- bflsc - fix brackets so [Chips] isn't always null
+- Remove unused variables.
+- Use cgcompletion timeouts for the unreliable shutdown functions on kill_work.
+- Fix cgcompletion return code and free on successful completion.
+- Provide a cg_completion_timeout helper function for unreliable functions that
+takes arbitrary functions and parameters and reliably returns.
+- Perform sync transfers on shutdown to allow final transfers to complete.
+- Destroy cgsems used after transfers to not leave open files on osx.
+- klondike rewrite work control
+- allow __work_complete() access
+- miner.h allow devices to tv_stamp work
+
+
+Version 3.6.3 - 17th October 2013
+
+- API add 'MHS %ds' to 'summary'
+- Optional lock tracking and stats via the API
+- Speed up polling repeat again in usb poll thread and handle async after the
+message to disable polling is complete.
+- Revert to using timeouts on !linux since libusb leaks memory without them.
+- Revert to libusb instead of libusbx
+
+
+Version 3.6.2 - 17th October 2013
+
+- Remove unused components of jansson
+- Remove unused parts of libusb
+- Work around older libtoolize that fails without top ltmain.sh not being
+present during autogen
+- Fix open coded use of autoreconf in autogen
+- Update jansson to only build parts we require and suited to our build
+environment.
+- Initial import of jansson-2.5
+- Prevent further USB transfers from occurring once the shutdown signal has been
+sent to prevent transfers getting stuck and libusb failing to shut down.
+- Make the USB polling thread poll every second to potentially aid longer
+timeout transfers.
+- Set device_diff on work in get_work to not be missed with drivers that use
+get_work directly.
+- Convert icarus driver to hash_driver_work model.
+- bflsc - also allow ' 0' in DEVICES IN CHAIN
+- bflsc - allow a 0 in DEVICES IN CHAIN
+- Add needed EXTRA_DIST for libusbx.
+- Update libusbx configure.ac changes.
+- Revert libusb Makefile changes from going to libusbx.
+- Fix trivial libusbx warnings.
+- Convert libusb-1.0.16-rc10 to libusbx-1.0.17
+
+
+Version 3.6.1 - 14th October 2013
+
+- Emulate the libusb_control_transfer sync setup in our async variant.
+- usbutils - make all libusb_error_name messages the same
+
+
+Version 3.6.0 - 14th October 2013
+
+- increasing max miners for avalon driver
+- using separate identifier for bitburner fury boards
+- changes to bitburner driver for bitburner fury boards
+- hexstr is too small in test_work_current
+- Windows uses errno for WSAETIMEDOUT
+- Convert the usb callback function to using cgsem_t timed waits to avoid race
+conditions with conditionals/mutexes.
+- Give correct return code in cgsem_mswait
+- Check for correct timeout error in cgsem_mswait
+- Fix util.h exports for cgsem_mswait
+- Implement a generic cgsem_mswait similar to sem_timedwait
+- Use the one LIBUSB_ERROR_TIMEOUT for cancelled transactions since this error
+is explicitly tested for in various drivers.
+- Do not use locking on usb callback function pthread signalling to prevent
+deadlock with libusb's own event lock.
+- Use a write lock when performing any USB control transfers to prevent
+concurrent transfers.
+- Free a libusb transfer after we have finished using it to avoid a dereference
+in usb_control_transfer
+- Do not perform bfi int patching for opencl1.2 or later.
+- Although async transfers are meant to use heap memory, we never return before
+the transfer function has completed so stack memory will suffice for control
+transfers, fixing a memory leak in the process.
+- klondike - correct/reverse min/max stats
+- api incorrect message name
+- klondike - use a link list queue rather than a circular buffer - and add
+timing stats
+- Use a timeout with usb handle events set to a nominal 200ms and wait for the
+polling thread to shut down before deinitialising libusb.
+- Use stack memory for hex used in stratum share submissions.
+- Use stack memory in test_work_current, avoiding a malloc/free cycle each time.
+- Provide a lower level __bin2hex function that does not allocate memory itself.
+- Convert the bitfury driver to use the hash_driver_work version of hash_work.
+- Add a hash_driver_work function to allow for drivers that wish to do their own
+work queueing and management.
+- Convert all usb control transfers to asynchronous communication with our own
+timeout management as well.
+- Klondike - increase circular read buffer size
+- Klondike - extra zero value and range checking in temp conversion
+- klondike - display MHz also
+- Make pthread conditional timeouts handle all bulk usb transfer timeouts
+performing libusb_cancel_transfer, disabling timeouts within libusb itself.
+- Avoid calling get_statline_before on exit to avoid trying to use it on drivers
+in an indeterminate state.
+- Avoid calling get_statline on exit.
+- Add a small amount to the usb timeout before cancelling to allow for a regular
+usb polling interval to pass.
+- Do not attempt to clear a usb halt before sending the cancel message since all
+transfers should normally be cancelled before attempting to clear a halt
+condition, and only change the return message to a timeout if it's consistent
+with a cancellation.
+- Retry up to USB_RETRY_MAX times to clear a halt condition before failing.
+- Show the error number as well as the description in erroring bulk transfers.
+- Drop logging level for failed to connect to stratum to verbose mode only since
+we hit it regularly.
+- We are always dependent on libusb handling events so use the blocking
+libusb_handle_events in the polling thread and use a bool to know if we should
+continue polling.
+- Use fractional hashrate return values in bitfury_scanhash to minimise the
+number of times we return 0 based on hashrate so far to further damp out
+displayed hashrate.
+- Check for presence of driver name in DRIVER_COUNT_FOUND to prevent strcmp on a
+null pointer when a driver is not built in.
+- CMR allow sending flash and clock commands
+- Kill off threads that have failed using hash_sole_work instead of just
+disabling them.
+- Make the bf1 getinfo size a macro
+- Failing to add_cgpu in bitfury should be a terminal failure.
+- Check return values when attempting to open a BF1 device and set the msg size
+as a macro.
+- Display errors on failed usb read and write and consider sequential IO errors
+a permanent failure.
+- Use libusb's own error name function instead of hand coding the error names.
+- Limit ms_tdiff to 1 hour as a sanity check.
+- Enable the usb buffer in avalon driver.
+- Check for async transfer variants of error messages.
+- Remove unused variables.
+- Try switching pools if for some reason we end up with only idle pools and have
+ended up current_pool set to an idle one.
+- Check a pool is stable for >5 mins before switching back to it.
+- Minimise the time between dropping the read devlock and grabbing the write
+devlock to avoid tons of logging spam in the interim.
+- Check for libusb transfer stall error to be consistent with async IO errors
+returned for a halt condition.
+- Check for continuous IO errors on USB and consider the device inactive if more
+than retry max.
+- Make the devlock a cglock in usbutils and only grab the write lock for
+fundamental changes allowing us to send and receive transfers concurrently
+without lock contention.
+- Prevent overflows in us_tdiff and ms_tdiff.
+- Change second initialise message on bitfury verbose mode.
+- Submitting an ntime offset nonce needs to be done on a copy of the work
+instead of the original so abstract out shared components as much as possible,
+minimising strdups in copy_work and make submit_work_async work take copied
+work, cleaning up code in the process.
+- Provide a way for drivers to submit work that it has internally rolled the
+ntime value by returning the amount it has ntime rolled to be added.
+- Typo in configure.ac
+- Remove unmaintained broken ztex driver.
+- Icarus - use a data structure for I/O rather than magic numbers
+- delete old tracked ccan/opt/*.o files
+- klondike correct cvtKlnToC() temperature calculation
+- klondike - correct 1st reply debug based on define
+- klondike - debug dump structured replies
+- klondike - avoid division by zero if maxcount is unexpectedly zero
+- klondike store and report errorcount and noise
+- klondike - fix chipstats api stats buffer overrun with 16 chips
+- klondike add new nonecount only once
+- klondike - report mh/s based on nonces found + put old estimate into API stats
+- klondike use a memcpy
+- klondike fix bracket tabs indenting
+- api.c missing Klondike from ASIC list
+- Klondike update code to current git
+- Add 2nd CMR to 01-cgminer.rules
+- Add Klondike to 01-cgminer.rules
+- Klondike to main directory
+- Klondike consistent code spacing
+- Klondike update driver code to current git
+- update firmware for 16 chips, add dist files
+- beta final 0.3.0 release
+- updated firmware, IOC method
+- prevent nonces when not state W
+- added driver config option support
+- fixes for 300 MHz, fix K1 parts list
+- update driver, docs
+- update firmware & utils
+- updated cgminer driver for 3.3.1
+- update firmware and driver, create new cgminer fork
+- update klondike driver
+- add cgminer driver file as-is
+- Add API output displaying USB cancellations.
+- Store statistics on how often we have to cancel async bulk transfers and add a
+debug message whenever we do.
+- Treat any unexpected timeouts waiting for async transfers as though there may
+be a usb halt condition and attempt to clear the halt before cancelling the
+tranfer.
+- Remove zero packet flag on usb as it's unsupported outside linux and
+unnecessary.
+- Fake the libusb transfer timed out message if we force cancel it with our own
+async functions.
+- Use asynchronous transfers for all bulk transfers, allowing us to use our own
+timers and cancelling transfers that take too long.
+- Add libusb error warning message when significant error occurs.
+- Icarus CMR2 detect FPGA setup
+- Disable bitfury device thread on it disappearing.
+
+
+Version 3.5.0 - 29th September 2013
+
+- Add magic init sequence required on BF1 devices to get them mining on windows.
+- usbinfo.devlock is only ever write locked so convert it to a mutex
+- Icarus remove unneeded opt_debug tests due to applog being a macro
+- Icarus - CMR shouldn't wait the full timeout due to handle sharing
+- We should only yield once in cg_wunlock
+- Provide a function to downgrade a cglock from a write lock to an intermediate
+variant.
+- Deuglify use of _PARSE_COMMANDS macro expansions.
+- Deuglify use of usb parse commands macro in usbutils.
+- Use the driver add commands macros in api.c to avoid individually listing
+them.
+- Separate out asic fpga and opencl drivers in the driver parse commands macro
+for use individually as needed.
+- Use macro expansion in usb_find_devices to avoid explicitly listing them all.
+- Use macro expansion to iterate over all the drivers without explicitly writing
+them out in usbutils.c
+- Iterate over the bitfury offsets in order of decreasing likelihood.
+- Reattach the kernel driver on linux on usb_uninit.
+- Attach the kernel driver on failure to usb init on linux.
+- libusb kernel driver operations are only available on linux.
+- There is no need to get the external prototypes for drivers in cgminer.c any
+more.
+- Remove unnecessary gpu_threads initialisation.
+- Put avalon last in the sequence of adding drivers to prevent it trying to
+claim similar chip devices on startup.
+- Use macro expansion to iterate over all device drivers without needing to
+explicitly code in support in all places. Pass a hotplug bool to the detect()
+function to prevent opencl trying to hogplug GPUs.
+- Forward declare all device drivers in miner.h avoiding the need to export them
+everywhere else.
+- Add a noop function for driver detect when it's missing.
+- Reuse the DRIVER_ macros to avoid having yet another definition for DRV_
+- Use macro expansion to generate extern device_drv prototypes.
+- Create a macro list of drivers to enable easier addition of further drivers.
+- There is no point setting the BF1 preferred packet size to the maximum since
+it will do so automatically.
+- icarus ensure all cmr interfaces are initialised properly
+- usbutils - fix USBDEBUG warnings
+- Remove unnecessary steps in communicating with BF1 and just use USB interface
+1.
+- usbutils - usb_bulk_transfer fix the buf/data fix
+- usb_bulk_transfer - use the allocated buffer
+- Set preferred packet sizes per interface on BF1.
+- usbutils allow PrefPacketSize per endpoint
+- Remove magic control sequences on open/close on BF1 and just flush the read
+buffers.
+- Check return codes in getinfo and reset and fail as needed in BF1.
+- Check return code for bitfury_open and release resources properly on failed
+initialisation.
+- Abstract out flushing of interrupt reads in BF1 devices.
+- Perform interrupt read after close message on BF1 as per serial close.
+- Perform interrupt read flush as per serial open on BF1 devices.
+- Add information for 2nd USB interface on BF1 devices and choose interface 1
+for bulk transfers.
+- usbutils - bulk transfer copy test fix
+- usbutils - add USBDEBUG for usb_bulk_transfer
+- Add more read_ii variants to usbutils.
+- Name remainder of BFU usb commands used.
+- Use submit_tested_work in bitfury driver to avoid unnecessarily re-testing the
+work for validity.
+- Abstract out work submission once it's been tested, to be used by drivers that
+do their own internal validity testing.
+- Store the hash2 array in struct work for further reuse.
+- usbutils - which_intinfo not requried
+- Use the test_nonce function within submit_nonce and store the uint32
+corresponding to hash2 37 for further use.
+- usbutils - interfaces must all be on one handle - ep implies the interface
+- avalon stats use exact type
+- Only set share diff if we've confirmed it's a share first.
+- Update ASIC-README for bitfury devices.
+- Use an array of offsets when checking nonces in bitfury_checkresults
+- Limit the duration we wait for reads in BF1 based on time already elapsed to
+account for other delays such as work restart messages or out of work.
+- Minimise size of serial string we copy in BF1 stats to avoid overflow.
+- Implement basic API stats for BF1 and increase array of results to check for
+the rare straggling result.
+- Space debug output for bf1 to separate from numerals.
+- Abstract out the bitfury open close and reset functions and use them on
+reinit.
+- Rename BF1 devices BF1
+- Check for work restart, breaking out early after usb reads in BF1.
+- Do not lose the first sets of results from BF1.
+- There is no point checking for results from the next round of work on BF1.
+- Last result returned by BF1 is an end of results marker so ignore it.
+- restart_wait should return 0 if thr_restart is true.
+- Remove unused code by bitfury driver since current driver uses serialised
+scanhash.
+- Meter out return of estimated hashes in BF1 to smooth out visible hashrate.
+- Optimise inner scanhash loop for bf1.
+- Add yet another backup work for triple buffering of work in bf1 to account for
+extra late results returned and don't check nonce offsets which appear to never
+return.
+- Name the work request and result usb commands for BF1
+- Define a mandatory upper limit to waiting for reset and data on BF1 based on
+full nonce duration.
+- Decrease usb buffering to verbose logging.
+- Add in first draft for a serialised work model sending/receiving data for BF1
+devices.
+- Add complete close sequence to bf1 as it happens on serial.
+- Provide a bitfury identify function for bf1.
+- Reliably extract BF1 information at startup and reset the device.
+- Add commands for getting BF1 bitfury info
+- Add magic BF1 bitfury open and close control sequences.
+- Add BF1 detection code to bitfury driver.
+- Create basic placeholders for bitfury driver code.
+- Add bf1 device information to usbutils to enable device detection.
+- Add basic defines for building for bitfury devices.
+- Add redfury device to udev rules.
+- avalon: display the FPGA controller version on API
+- pool_active uninitialised_var rolltime
+- Use macro expansion to only need to define usb enums and commands in one
+place.
+- usbutils saving incorrect overflow buffer
+- ignore libusb.la and *.lo on linux
+- icarus support CMR with no extensions
+- usbtils - interfaces dont work yet in libusb windows so disable for that only
+- Provide a --disable-libcurl config option to build support for stratum mining
+only.
+- Fix the api-example.c compile under Linux
+- usbutils - only release the device once - for the first intinfo
+- usbutils set_interface is no longer valid
+- ubsutils interfaces much each have their own handle
+- usbutils kernel_detach should use the interface number
+- usbutils - allow the driver to change which_intinfo
+- Reset quotas on load balance for all pools at the same time to avoid running
+out during selection and unintentionally dropping to fallback.
+- Break out of select pool from a common point for appropriate debug messages
+and to avoid further tests.
+- usbutils correct/reverse CMR product numbers
+- usbutils specifically track handles and interfaces
+- change drivers to use usb_interface() - required for multi interface change
+- usbutils - allow a device to use multiple interfaces (and better var names)
+- Cast -1 to (char) to cope with different default char types on ARM.
+
+
+Version 3.4.3 - 13th September 2013
+
+- Put corefoundation and iokit separate in ldflags for darwin.
+- Add rules for libusb Makefile.am building on osx
+- Add flags for building libusb statically on osx.
+- Find the greatest common denominator in quotas and use the smallest number of
+consecutive work items per pool in quota load balance mode to smooth hashrate
+across pools with large quotas. Give excess quota to priority pool 0 instead of
+pool 0.
+- Avoid dynamically adding stack memory for nonce2 in the stratum send thread
+and check the pool's nonce2_len will not cause an overflow.
+- Add subdir-objects to automake options.
+- Use inet_addr instead of inet_network to fix windows build.
+- Remove unused pbase variable.
+- Add support for socks4/4a proxies with stratum, and drop back to socks4
+support via the global --socks-proxy command to not break previous
+configurations.
+- Fix warning on mingw build.
+- Only show long-poll message in pool summary if it's not using stratum.
+- Increase the time for the waiting for work message to be given to be greater
+than that required for a pool swap in the scheduler which is set to 5s.
+- Change message in status when using a balanced pool strategy to notify if
+there's a stratum pool as well.
+- Use the --failover-only flag to have special meaning in combination with
+load-balance mode to distribute any unused quota back to pool 0 to maintain
+ratios amongst other pools.
+- Display quota and allow it to be modified via the pool menu.
+- Add API commands and modify output to support pool quota displaying and
+changing.
+- Change message in status when using a balanced pool strategy to notify if
+there's a stratum pool as well.
+- Add quota support to configuration files.
+- Rotate pools on all failures to set a pool in select_pool.
+- Use quotas for load-balance pool strategy.
+- Provide a mechanism for setting a pool quota to be used by load-balance.
+- Use the --socks-proxy option with stratum, changing it to defaulting to socks5
+and give appropriate message should it fail to connect.
+- Cope with trailing slashes in stratum urls.
+- Add more debugging messages when negotiating with proxies for stratum.
+- Test specifically for socks5h in socks support for stratum.
+- Add support for socks5 proxy with stratum
+- Provide support for negotiating a stratum connection via http proxies.
+- Connect to the proxy URL and port if specified for stratum sockets instead of
+the pool directly.
+- Extract any proxy url and port to be used by sockaddr if possible using
+extract_sockaddr.
+- Make extract_sockaddr set variables passed to it rather than pool struct
+members.
+- miner.php sort the mcast rigs so they are always in the same relative order
+- miner.php allow sending the muticast message multiple times
+- miner.php mcast ignore duplicate replies
+
+
+Version 3.4.2 - 3rd September 2013
+
+- take_queued_work_bymidstate should use a write lock.
+- miner.php coding warning
+- miner.php disable 'gen' by default
+- miner.php allow formula generation of new fields
+- miner.php add doctype
+- miner.php remove incorrect echo
+- miner.php optional error if not enough mcast rigs are found
+
+
+Version 3.4.1 - 31st August 2013
+
+- API mcast add a description option with miner.php
+- Always use a maxpacketsize buffer in usb_bulk_transfer
+- bflsc ensure getinfo cannot overflow it's storage buffer
+- Don't decref json values in stratum parsing due to memory corruption.
+- Use 64 bytes for all libusb control transfers.
+- Skip dissecting opt->names in parse_config if it doesn't exist.
+- Use an internal buffer in _usb_transfer_read in case the read is larger than
+the buffer passed to it.
+- ICA optional limit timing with short=N or long=N
+- Revert to old custom tolines function since strtok_r is not portable.
+- bflsc remove unused commented out code
+- logging - code mistake
+- logging - applogsiz() for large messages
+- Provide base structures for getaddrinfo.
+- Include string.h in bflsc driver.
+- Get rid of linear removal of spaces in bflsc text parsing and use strstr
+throughout instead.
+- Use reentrant strtok in tolines() function in bflsc to avoid racing on
+contextless calls.
+- Show how small a too small result in bflsc is.
+- Duplicate the buffer in process_results in bflsc since strtok modifies it
+making debugging output limited to one line.
+- Only process nonces in bflsc if the breakdown function succeeds.
+- Ignore zero count messages in bflsc instead of trying to parse them.
+- Return ok in tolines when it doesn't match inprocess message for bflsc.
+- Remove inprocess line instead of deleting all following responses in bflsc.
+- Change ok testing logic in breakdown() in bflsc and return if not ok at any
+stage.
+- Check the return value of tolines in bflsc driver.
+- Use strtok to parse lines in bflsc driver.
+- Add libusb-1.0 m4 directory and gitignore file.
+- Properly convert from ranlib to lt_init in configure.ac
+- Make autoconf always build for libusb.
+- More autoconf fixes.
+- Unconditionally build jansson statically from the cgminer source tree.
+- Only test for all usb devices once in configure.ac
+- Fix various libusb warnings and possible bugs on linux build.
+- Add make clean and maintainer-clean to autogen
+- Remove examples from libusb Makefile and generated autoconf files.
+- Fix libusb subdirectory builds.
+- Remove cached files from libusb autoconf on running autogen.sh
+- Remove unused HAVE_LISBUSB macro and use USE_USBUTILS everywhere.
+- Use direct auto* files to avoid failure of autoreconf
+- Remove unused and maintainer cleaned files
+- Show RT_LIBS in ./configure output.
+- First import of libusb-1.0
+- bflsc xlinkstr use snprintf
+- Fix win32 build.
+- Use take_queued_work_bymidstate in the bflsc driver to avoid the rare chance
+repeated results come back from the same work item.
+- Provide a funcion that looks up queued work by midstate and then removes it
+from the device hash database.
+- Fix no -rt library on darwin.
+- Update included jansson to v2.4
+- Fix OSX build.
+- Provide an osx fix for cgtimers and a fallback to timevals for all other
+platforms !linux !win32 !osx.
+- Move two more timer functions out of define macros to enable them to be used
+by future osx code.
+- cgtimer_sub is now the same since cgtimer_t should be the same on all
+platforms.
+- miner.php fix missing global
+- Only count submitted nonces as diff1shares if they're valid.
+- Substantially raise the maximum avalon frequency for water-cooled, over-volted
+designs.
+- Compile MCast.java with an old java
+- API Multicast sample MCast.java+MCast.class
+- BTB show C/MHz/mV for device
+- api.c remove unused reply string
+- api.c fix mcast debug message bug
+- miner.php implement API Multicast handling to automatically find your local
+net miners
+- API mcast only reply to remote IP's that are allowed access
+- Initial API Multicast response v0.1 to find cgminer APIs
+- Use timespecs on windows as cgtimer_t to capitalise on the higher resolution
+clock changes.
+- Abstract out the conversion of system time to an lldiv_t in decimicroseconds.
+- Use our own gettimeofday implementation on windows for it to be consistent
+across ming builds and higher resolution.
+
+
+Version 3.4.0 - 21st August 2013
+
+- Use stack data for HW error% in avalon stats.
+- Add avalon HW error% to stats and only show BTB variables if avalon is a BTB.
+- Check for cnx_needed on each loop through wait_lp_current.
+- Return positive for cnx_needed when no_work is true.
+- Stratum is used more often so test for it first.
+- Reorder support names alphabetically.
+- Only display the no pool work message once if there are multiple waiters in
+hash_pop
+- Provide a message and set a bool when no work is available from any pools and
+when it resumes again.
+- We don't want to continue into the hash_pop function if the getq is frozen.
+- Only report threads in and out in queued work devices across a get work since
+the rest happens asynchronously and the get work is what the device might be
+waiting on.
+- Thread reportin and out can be static non inline.
+- usbutils cps sleep_estimate is not an underestimate
+- usbutils add cps stats estimates
+- Provide cgtimer_sub helper functions.
+- Provide cgtimer_to_ms helper functions.
+- Rename cgsleep_prepare_r as cgtimer_time to get time in cgtimer_t format and
+call cgsleep_prepare_r as a macro for cgtimer_time
+- Use the reentrant cgsleep functions for usecps in usbutils.
+- TimeBeginPeriod and TimeEndPeriod do not add significant overhead when run the
+entire time for cgminer so avoid trying to maintain balanced numbers of them for
+specific time calls to simplify code.
+- Replace all references to the old n*sleep functions with the equivalent
+cgsleep_*s replacements.
+- timeGetTime uses huge resources on windows so revert to using timevals for its
+implementation of cgtimer_t
+- Quotient/remainder error in ms division.
+- Only grab a queued work item if we successfully grab the lock to submit work
+in bflsc_send_work
+- BTB get version from Firmware
+- Carve out the unused portions of sha2 implementation.
+- Import Aaron D. Gifford's fast sha256 implementation.
+- Increase the que_low watermarks on BFLSC for they are too low to keep the
+device busy on scanwork loops.
+- Provide cgtimer_to_timeval helper functions.
+- Provide a timeval_to_cgtime helper function to reuse values.
+- Check for thr->work_restart in restart_wait.
+- We should be using que_low to decrease scan sleep time in bflsc.
+- Prepare sleep time on bflsc if no dev needs work yet to avoid busy waiting.
+- Simplify cgsleep code for windows by using a typedef for cgtimer_t that
+resolves to clock resolution, using that internally.
+- On windows use the higher accuracy timegettime function to really get 1ms
+clock and timer accuracy.
+- Use the cgsleep reentrant function to sleep for bflsc between read results to
+account for time taken to perform reads.
+- Use 100ms delay between checking for results on all bflsc devices as the
+buffering of results mean checking more frequently just wastes CPU and causes
+more lock contention for only marginally better latencies.
+- Fix missed endtimeperiod in overrun timer on windows.
+- Make cgsleep_us_r take an int64_t for us.
+- Make the cgsleep functions build on windows.
+- Use the cgsleep reentrant function in avalon_send_task.
+- Use the reentrant cgsleep functions within the avalon_send_tasks function.
+- Set high resolution timing on windows within the cgsleep functions.
+- Use the reentrant cgsleep function to time sleeps on reading from avalon.
+- Provide reentrant versions of cgsleep functions to allow start time to be set
+separately from the beginning of the actual sleep, allowing scheduling delays to
+be counted in the sleep.
+- Make the nmsleep and nusleep functions use the new cgsleep functions
+internally till functions are migrated to the new cgsleep API.
+- Add a ms_to_timespec helper function, and create a cgsleep_ms function that
+uses absolute timers with clock_nanosleep to avoid overruns.
+- Add rt lib linkage to enable use of clock_nanosleep functions with older
+glibc.
+- Add necessary time header include to avalon driver.
+- Do a sleep of the full duration it would take to do all the work using
+clock_nanosleep in avalon_send_tasks to avoid sleep overruns before polling to
+see if it's ready.
+- Add a timeraddspec helper function.
+- Provide a us_to_timespec helper function.
+- Use the us_to_timeval helper function in the avalon driver.
+- Provide a us_to_timeval helper function.
+- Use timeval_to_spec helper in avalon driver.
+- Add helper functions to convert timespec to timeval and vice versa.
+- simplifying buffer full check
+- forking bitburner write thread function
+- making sure original Avalon is unaffected by BitBurner changes
+- changes to queueing strategy for BitBurner boards
+- Do not poll in avalon_get_results without sleeping if we have finished parsing
+a full result.
+- Add c to ambient temperature display for avalon driver.
+- BTB allow up to 1400mV as per firmware limits
+- avalon for timeout allow d='calculate it' and fix uninitialised
+- Use cloned work when finding avalon results since another thread can discard
+the work item while it's in use.
+- Provide a variant of find_work_bymidstate that returns a clone of the found
+work.
+
+
+Version 3.3.4 - 14th August 2013
+
+- API/miner.php add some % fields
+- Nonce2 stratum submission is not working with nonce2 lengths >4, revert the 
+buggy __bin2hex function and use bin2hex.
+- The write thread in avalon is only ever actually woken up by timeout so remove
+the write semaphore and use a simple sleep poll.
+- Fix warning.
+- Interrupting reads on the avalon to start writes loses data so remove the 
+cgsem_post in the read code.
+- Add room for the null byte at the end of the nonce2 string on stratum share 
+submission and zero the allocated ram.
+
+
+Version 3.3.3 - 13th August 2013
+
+-  Only perform the bin2hex on nonce2 data if it's required for stratum 
+submission, thereby removing the last conversion of that type from stratum work 
+generation.
+-  Create a work data template when receiving stratum notification, allowing a 
+simple memcpy of the merkle root avoiding more hex2bin conversions on each work 
+generation.
+-  Export the workpadding char in miner.h
+-  Avoid a potential overflow should a pool specify a large nonce2 length with 
+stratum.
+-  Avoid one more hex2bin in gen stratum work.
+-  Rename work gbt_coinbase to coinbase to be in line with pool variable name.
+-  Perform merkle bin hex2bin on stratum notify to avoid doing it on each work 
+generation.
+-  Reuse just the one pool coinbase variable in stratum, avoiding more string 
+functions and storage in gen_stratum_work on each work generation.
+-  Rename pool gbt_coinbase variable to coinbase to combine it with the stratum 
+coinbase data.
+-  Use a nonce2 offset variable for both gbt and stratum to consolidate 
+requirements on work generation.
+-  Merge pull request #474 from kanoi/master
+-  util.c update quit call for new functions
+-  use correct define for OSX in util.c
+-  miner.h inline semaphores increase information on failure
+-  util.c expand quit to show file/func/line
+-  Merge remote-tracking branch 'conman/master'
+-  Cache as much of the gbt coinbase as possible to avoid doing unnecessary 
+hex2bin conversion on every work generation with gbt.
+-  We should be using a cg_wlock initially in generating stratum and gbt work 
+before downgrading the lock.
+-  Add the ability to downgrade a write variant of the cglocks.
+-  Fix --scrypt being required before scrypt intensities on command line or not 
+working at all via config files.
+-  Cache the hex2bin of pool nonce1 in stratum, avoiding hex2bin on each work 
+generation.
+-  Cache the binary generation of coinbase1 and 2 on stratum, avoiding a hex2bin
+of coinbase1 and 2 on each work generation.
+-  cgsem - increase information on failure
+-  avalon init write_sem before use
+
+
+-  3.3.2 - 9th August 2013
+
+-  Fix uninit variable warnings.
+-  usbutils - force check every combination
+-  Fix warning.
+- Recreate curses windows on windows when a device is hotplugged to allow window
+resizing without crashing.
+- Update copyright notice.
+- Limit intensity range according to whether scrypt is in use or not.
+- Do not allow benchmark mode to be used with scrypt.
+- Add a --bflsc-overheat command which allows you to set the throttling 
+temperature for BFLSC devices or disable it.
+- Move bflsc defines to a header file.
+- avalon allow frequency to be set via the API
+- BTB voltage management via the API - and set default on startup
+- Avalon BTB allow partial work to be transferred
+- avalon_cts use correct buffer
+- miner.php format Best Share
+- remove unnecessary memcpy
+- using more concise description
+- using usb_ident
+- forgot a return
+- changes to Avalon driver for BitBurner boards
+- Revert "Sleep after sending icarus work to emulate working at 115200 baud."
+- api correct timeout stat display
+- usb timeouts - min/max also
+- log USB timeouts in API stats
+- usbutils report failed timeouts
+- usbutils ensure stats macros are using the macro arguments
+- Check for negative wait time in socket_full.
+- Fix extra argument passed to statline before.
+- Adjust socket wait timeout in recv_line according to how long we've already 
+waited to avoid a 60 second wait dropping to 1 second due to a blocked socket.
+- usbutils use a heap buffer for bulk read rather than stack
+- usbutils only one bulk transfer call per stat
+- set device_drv function noops when first add_cgpu
+- usbutils - in init only change the config if needed
+- bflsc nonce per work item stats
+- bflsc increase flush count to handle parallel work
+- force type checking on curses
+- logging - size check sprintf
+- usbutils - size check all sprintf
+- cgminer - size check all sprintf
+- size check get_datestamp/get_timestamp and remove unused cgpu->init
+- make all statline overflow safe
+- WU only needs +2 width
+- Check for a timeout in avalon_scanhash and post to the write sem if we receive
+one.
+- Decay result count in avalon more slowly to not falsely detect idle periods as
+low result return rates.
+- Count the number of miners idled in avalon to account more accurately for when
+its result return rate is too low.
+- Fix potential dereference when starting avalon with all new work.
+- Convert the decay_time function into one that truly creates an exponentially 
+decaying average over opt_log_interval.
+- Only throttle avalon clockspeed in avalon_auto in non optimal temperature 
+settings if the fanspeed has reached maximum.
+- Reinstate more aggressive <2% HW error target for avalon-auto
+- Set avalon fan min and fan max to PWM values instead of percentage.
+- Provide an --avalon-freq command line to give a valid range of frequencies for
+avalon in auto mode.
+- Set the avalon idle frequency to lowest if avalon auto is enabled and we have 
+an overheat condition.
+- Decrease avalon frequency in auto mode if we are unable to maintain the 
+temperature in the optimal range.
+- Don't count invalid nonces as hashrate for bflsc.
+- Use a more conservative upper limit of 1% for hardware errors with avalon auto
+frequency.
+- Allow the avalon fanspeed range to be passed as parameter on the command line,
+default to 20-100%
+- Just display A: and R: for difficulty accepted and rejected to preserve screen
+real estate and decrease decimal places for WU.
+- correct device DR: and remove global U:
+- Update all screen A/R to instead use DA/DR and device U to WU
+- miner.php add ASC fields
+- GPU fan rpm display 9999 when it overflows
+- bflsc get volts stats needs its own GETVOLTS
+- Support all avalon frequencies on the command line.
+- Move to slightly more relaxed timeouts for avalon.
+- MMQ turn on cps delays
+- bflsc x-link header different to documentation
+- Reset the other auto counters in avalon when idling a device.
+- usbutils/icarus include more locking to usbdev access
+- Icarus turn on cps delays by default
+- usbutils cps correct time measurement
+
+
+Version 3.3.1 - 25th June 2013
+
+- Add an avalon-auto option which enables dynamic overclocking based on hardware
+error rate for maximum effective hashrate.
+- Add an --avalon-cutoff feature which puts the avalon idle should it reach this
+temperature, defaulting to 60, re-enabling it when it gets to target
+temperature.
+- Change default avalon target temperature to 50 degrees.
+- usbutils - incorrect test for * in bus:dev
+- Redo +1 fix in bflsc.
+
+
+Version 3.3.0 - 24th June 2013
+
+- Add an --avalon-temp option to allow a user specified target temperature.
+- Demote no matching work message to verbose logging only on avalon.
+- Make the fan control on the avalon a simple PID controller with a target
+temperature of 45.
+- Demote bflsc hw error messages to verbose logging only.
+- bflsc - handle xlink timeouts by having generic IO functions
+- Demote the invalid nonce warning to log info.
+- Ignore iManufacturer for BFLSC devices since the device name will still match
+and some unbinned chips are missing it.
+- sc_count shouldn't be +1 in bflsc.
+- Use the info timeout for read_nl in getidentify bflsc.
+- Add a usb_read_nl_timeout macro.
+- bflsc try getinfo twice
+- set MSG_ASCUSBNODEV always defined
+- Hard code the preferred packet size for AMU, BLT and ICA.
+- API V1.26 update ASIC support
+- Icarus enable the read buffer for the detect nonce
+- Support new overclocking speeds for avalon: 325, 350 and 375
+- undo icarus show errno, put it as debug in ubsutils
+- icarus add errno to rerr and werr
+- Sleep after sending icarus work to emulate working at 115200 baud.
+- Use the nusleep function for sleeping after sending work in avalon.
+- Show an integer only for diff if it is one.
+- Set the avalon preferred packet size to 512.
+- Reinstate the maxPacketSize determined by the end descriptor but allow the
+driver to override it.
+- Only update hashmeter if we have done hashes or haven't updated longer than
+the log interval, fixing a us/ms error.
+- Use only one cgsem in avalon signalling when the write thread should commit
+work by reading the status bytes off during an avalon_read, minimising the
+number of usb calls and resetting from only one place.
+- Change avalon no valid work message to no matching work to match API
+terminology.
+- Use low latency usb transfers on the avalon, sleeping up to half a buffer's
+worth only if no data is returning to increase hashrate, abolish lost work and
+decrease CPU.
+- Minimise the sleep times in avalon read to avoid result loss.
+- Use a half nonce range before cycling through avalon's scanwork to ensure it
+gets a chance to fill work if time is tight for the write thread to signal a
+wakeup.
+- Temporarily limit usb transfer sizes to 512 till we provide a way for each
+driver to choose the upper limit.
+- Increase watchdog sick time to longer than it takes for a pool to be detected
+dead.
+- Limit USB transfers to the max size reported by the descriptors.
+- Increase the BFLSC timeout to allow the maximum number of results to be
+returned for BAS in time.
+- Decrease BAL and BAS latency to be just larger than one result read.
+- disable curses device resize that crashes on windows
+- BFLSC latest firmware has its own thermal cutoff set to 90, so use the same
+value in case we have an old firmware that isn't throttling by itself.
+- Drop watermark low limits for bflsc.
+- Set the fanspeed on bflsc to max if we don't know the temperature.
+- Use a low watermark for queueing mandatory work on bflsc instead of zero.
+- Only mandatorily grab the bflsc mutex on submitting work when the queue is
+empty.
+- Adjust bflsc v2 watermarks.
+- Only increase sleep time on bflsc if the queue isn't emptying at all over the
+sleep duration.
+- Fix warning.
+- bflsc yet more API stats
+- bflsc add some more API stats
+- bflsc correct firmware matching
+- bflsc correct comment
+- Fixed Commands with No params
+- bflsc driver support for v2 firmware
+- Odd Issues
+- Fixed Python Example
+- Added Python Api Example
+- Added Python Api Example
+- Multiplier fail for microseconds vs milliseconds when updating hashmeter in
+hash_queued_work.
+- Only make threads report in/out across the actual driver code and update their
+status on reporting out as well as in.
+- usbutils initialise close key/sem
+- usbutils cleanup linux semaphores on release
+- Difficulty should be unconditionally byteswapped, not swapped to big endian.
+- We should be setting cancelstate, not canceltype when disabling it for usb
+locking.
+- Pthread cancel state should be set to disable on usb DEVLOCK.
+- Fanauto on bflsc is Z9X according to the source code, not 5 as per the draft
+protocol document.
+
+
+Version 3.2.2 - 16th June 2013
+
+- Record and report USB pipe errors via API stats
+- Suspend stratum connections when we know they've failed and don't try to recv
+data from them once the socket no longer exists.
+- Pipe error is quite common on usb3 so drop logging to verbose level only.
+- ocl.c fix applog warnings on windows
+- applog/quit fix GPU errors created
+- usbutils - DEVLOCK other usbdev access
+- applog usb device list can be > LOGBUFSIZ
+- fix windows log warnings
+- logging remove extra added <LF>
+- remove varargs from logging/quit/in general as much as possible
+- Don't yield when downgrading a cg ilock.
+- Don't yield on grabbing the read lock variant of cglocks.
+- Off by one error in device count for display.
+- Don't display devices beyond the most_devices count in the curses status.
+- Only display as many device rows as the maximum live existed at any time.
+- usb lock out use cg locks
+- usb lock out transfers during open/close
+- Add error message to libusb pipe error
+- Differentiate libusb control transfer pipe errors from transfer errors since
+they're not fatal.
+- Create a usb_bulk_transfer wrapper for libusb_bulk_transfer to cope with pipe
+errors.
+- Only show efficiency in pool information for pools that don't support local
+work generation.
+- Create a pool_localgen bool function for testing when a pool can generate work
+locally.
+- ignore file that is generated on Macs
+- compile unix code on Mac OS X fixes not finding the config file in $HOME
+- Use mining start time for device MH/U calculations
+- Decrease the sleep duration before reading in avalon to not let the read
+buffer overflow.
+- Failure to read and write on pseudo semaphores on apple happens routinely on
+shut down so should not be a quit error, just a warning.
+- Unlock usb dev lock in the same place in usbutils.
+- Sleep if the avalon buffer is empty and we've requested a read to allow the
+write thread to take precedence.
+- Yield after releasing a lock in case we are on a device with limited CPU
+resources.
+- Add the cgpu_info structure before avalon reset.
+- Tidy up DEVLOCK/UNLOCK to have consistent use of the pstate variable without
+needing brace level match.
+- Icarus driver elaspsed timeout shouldn't be just USB I/O
+- usbutils avoid leaving devlock locked when thread cancelled
+- MMQ fix nodev failure caused by changes
+- ubsutils lock all access to nodev and cgusb
+- USB make device_path handled by usbutils
+- tidy up free in device detect functions
+- USB control creation and free of cgpu
+- Add FAQ regarding Work Utility.
+- Throttling the BFLSC at 80 seems to prevent generating garbled responses of
+higher temps.
+- Return after failed bin2hex conversion in bflsc.
+- Demote failed hex2bin result to LOG_INFO and check return result in
+driver-bflsc to avoid doing find_work_by_midstate.
+- Set BFLSC fan speed coarsely to keep it under 60 or auto as per specs saying
+it tries to stay below 60.
+- Limit usbutils LATENCY_STD to 32ms to keep transfers under 512 bytes.
+- Move macro definition to bflsc driver
+- Use a longer timeout for retrieving bflsc details.
+- Add a usb_read_ok_timeout wrapper to cope with slow init'ing devices.
+- cgsem_post after creating the thread info
+- Fix build.
+- Use cgsem structures instead of the flaky pings in the work queue to start
+mining threads and remove the unused thr_info_freeze function.
+
+
+Version 3.2.1 - 7th June 2013
+
+- Shorten the avalon statline to fit in the curses interface and show the lowest
+speed fan cooling the asic devices.
+- Set usbdev in usbutils after checking for nodev to avoid trying to access a
+dereferenced value.
+- AMU usbstatus correct name from enable UART
+- Icarus AMU enable the UART
+- Only libusb close if libusb release succeeds.
+- Failed reads and writes on cgsem_post and cgsem_wait should be extremely rare.
+- Implement cgminer specific cgsem semaphores to imitate unnamed semaphore
+behaviour on osx which does not support them.
+- Set cgusb->buffer to NULL when doing usb_buffer_disable.
+- Temporarily fix apple not having semtimedop by ignoring the timeout value.
+- BFLSC enable buffered USB reading
+- Icarus use buffered USB reading
+- bflsc & icarus use usb_ftdi_set_latency
+- usb_ftdi_set_latency LOG_ERRs if called incorrectly
+- add usb_ftdi_set_latency
+- usbutils optional read buffering
+- Set the avalon read transfer latency to avoid sleeping when no data is
+returned after very short latency settings.
+- correct bflsc BFLSC_BUFSIZ max calculation
+- Fix build for !curses
+- restore max code - since timeout is unsigned
+- compile warning - remove unused max
+- usb set FTDI latency higher to minimise status bytes
+- Check for zero timeout on _usb_write.
+- Check for zero timeout in usb read.
+- Define a minimum polling time based on frequency of mandatory updates of ftdi
+responses at 40ms.
+- Sleep right up to the timeout instead of the first half if we find ourselves
+polling in _usb_read
+- Enforce half timeout sized sleeps in usb_read if we find the device is not
+respecting libusb timeouts to avoid polling frequently.
+- Add more ASIC documentation.
+- Update README
+- Remove start device limitation on log window size to allow it to get larger
+with hotplugged devices.
+- Switch logsize after hotplugging a device.
+- Change switch_compact function name to switch_logsize to be used for other
+changes.
+- Only adjust cursor positions with curses locked.
+- devs display - fix GPU duplicate bug
+- Do not hotplug enable a device if devices have been specified and the hotplug
+device falls outside this range.
+- Change the --device parameter parsing and configuration to use ranges and
+comma separated values.
+- basic copyright statement in API.java
+- devs display - show ZOMBIEs after all others
+- Modify scrypt kernel message.
+- Check for pool_disabled in wait_lp_current
+- usbutils semun use proper def for linux which fixes OSX also
+- Check for pool enabled in cnx_needed.
+- Icarus add delays during intialisation
+- Update documentation.
+- Update copyrights of modified files.
+
+
+Version 3.2.0 - 31st May 2013
+
+- Add FAQ about windows USB keyboards and hotplug interactions.
+- Fix mingw build warnings in icarus driver.
+- Make usb_ftdi_cts use the _usb_transfer_read function.
+- Update ASIC-README with avalon info regarding default behaviour.
+- Break out of idling loop in avalon_idle if the buffer is full.
+- Provide some defaults for avalon if none are specified and do not try to claim
+the device if it fails to reset with them and no options are specified.
+- usbutils automatically track IO errors
+- usbutils allow a short wait for resources to be released
+- correct semaphore timeout comment
+- Set the fanspeed to the nominal chosen for GPUs.
+- Inverted sem_init logic.
+- Document avalon options in ASIC-README
+- Do avalon driver detection last as it will try to claim any similar device and
+they are not reliably detected.
+- Clamp initial GPU fanspeed to within user specified range.
+- Use a counting semaphore to signal the usb resource thread that it has work to
+do.
+- Avalon fan factor is already multiplied into the info values.
+- Get rid of zeros which corrupt display.
+- Logic fail on minimum fanspeed reporting.
+- Provide a workaround for fan0 sensor not being used on avalon and pad fan RPM
+with zeros.
+- Add ambient temp and lowest fan RPM information to avalon statline.
+- Display max temperature and fanspeed data for avalon.
+- Set devices to disabled after they exit the hashing loops to prevent the
+watchdog thread from trying to act on them.
+- Add avalon driver to hotplug.
+- Shut down the avalon mining thread if the device disappears.
+- Check for no usb device in usb_ftdi_cts
+- Check for valid usbdev in _usb_read in case the device has been unplugged.
+- Scanhash functions perform driver shutdown so don't repeat it.
+- Change the opencl shutdown sequence.
+- Send the shutdown message to threads and do the thread shutdown functions
+before more forcefully sending pthread_cancel to threads.
+- Use the cgpu_info shutdown to determine when to stop the avalon read and write
+threads.
+- Use semaphores to signal a reset to pause the read thread while the write
+thread does the actual reset, making all writes come from the same place.
+- Remove now unneeded fgpautils.h include from avalon.
+- usb_transfer_read should also not play with the endianness.
+- Use the USB wrappers for avalon, telling usbutils that we want the raw data.
+- Use separate ep for avalon tasks vs avalon reset and do not loop in write
+indefinitely.
+- Remove unneeded function and checks in avalon write code.
+- CMR handle baud options
+- work_restart is reset within the queued hash work loop.
+- Fix avalon shutdown sequence.
+- Execute driver shutdown sequence during kill_work.
+- Use nusleep in avalon_get_results in place of nmsleep.
+- Provide an nusleep equivalent function to nmsleep.
+- usb/ica add more (incomplete) CMR settings
+- Give a buffer of perceived results in avalon during idle periods to allow for
+results once it becomes active again.
+- libusb_control_transfer are meant to be endian specific, but host endianness
+so no conversion is needed.
+- Reuse old MTX Handle
+- usbutils check all memory allocation
+- usb separate thread for resource locking and modified windows locking code
+- Icarus report data direction with comms errors
+- Set the read and write threads for avalon to not cancel within libusb
+functions and wait for the threads to pthread_join on shutdown.
+- Offset needs to be incremented after avalon reads.
+- Make the avalon_read function parse the ftdi responses appopriately.
+- Use the avalon read timeout to completion if no data has been read.
+- wait_avalon_ready should only be used before writes.
+- Ask for the correct amount to read in avalon get results.
+- Spawn the avalon read thread first with info->reset set to discard any data
+till work is adequately queued.
+- Use direct usb read commands to avoid ftdi data being automatically cut off in
+avalon reads.
+- Do a simple usb_read_once for the avalon result from a reset command.
+- Make sure avalon is ready to receive more usb commands before sending them.
+- Implement avalon_ready and avalon_wait_ready functions for when usb is ready
+to receive commands.
+- avalon_read should not loop but just return whatever it has succeeded in
+reading.
+- Set avalon_info to device data void struct.
+- Specify avalon in avalon_reset.
+- First pass rewriting serialdev into direct usb dev for avalon driver.
+- Define a cts equivalent for direct usb and use it for avalon driver full.
+- Compile usbutils into avalon driver.
+- Check results come in at least at 2/3 the rate they should be on avalon and if
+not, reset it.
+- Give a warning but don't reset if the avalon buffer is full early.
+- Discard any reads obtained from the avalon get results thread during a reset.
+- Differentiate initial reset in avalon from subsequent ones.
+- Perform a mandatory reset if the avalon buffer signals it's full before it has
+queued its normal quota of work.
+- Wait till buffer is cleared after sending idle tasks to avalon before
+returning from avalon_idle.
+- Lock qlock mutex during reset from read thread in avalon to prevent more work
+being sent till the reset is over.
+- Reset avalon if we continue to be unable to send all the work items.
+- Add avalon reset response to debugging output.
+- Do a wait_avalon_ready before sending a reset code.
+- Iterate over spare bytes in the avalon result returned from a reset request
+trying to find the beginning of the reset.
+- Idle avalon after reset.
+- Check for nothing but consecutive bad results on avalon and reset the FPGA if
+it happens.
+- Make submit_nonce return a bool for whether it's a valid share or not.
+- Unset the work restart flag sooner in avalon_flush_work to avoid re-entering
+the flush work function and just reset the queued counter instead of rotating
+the array to avoid runs of no valid work.
+- Implement an avalon_flush_work function for work restarts.
+- Shut down avalon read and write threads and idle the miners on closing it.
+- Tighter control over work submissions in avalon allows us to use a smaller
+array.
+- Rotate avalon array to reset the queued count before releasing the lock so
+work will always be available on next pass.
+- Move avalon read thread start till after conditional wait, store idle status
+in avalon_info and use it to determine whether an error is appropriate or not.
+- Wait till the avalon_send_tasks thread has filled the avalon with idle work
+before starting the avalon_get_results thread.
+- Use AVA_GETS_OK macro in avalon_read.
+- Do all writes on avalon with a select() timeout to prevent indefinite blocking
+and loop if less than desired is written.
+- Check explicitly that ava_buffer_full equals the macro.
+- Send initial reset as an avalon task to remove avalon_write function.
+- avalon_clear_readbuf is no longer required.
+- Check for 2 stray bytes on avalon reset.
+- Create a separate thread for handling all work and idle submission to the
+avalon which messages the scanhash function it has completed to update
+statistics.
+- usbutils ensure it compiles without stats
+- usbutils include transfer mode in usbstats
+- Give the avalon get results thread name the device number as well.
+- Make sure we're not adjusting temps on every successful work retrieval on
+avalon.
+- Count missing work items from behind a successful work read in avalon as well.
+- Change message for work not found in avalon parser.
+- usbutils handle bulk_transfer partial writes
+- Simplify debugging and only discard from avalon read buffer if at least one
+full result has been discarded.
+- Only display discarded bytes in avalon if they're not used as nonces.
+- Only loop once through avalon_parse_results, but do so after timeouts as well.
+- Only debug and move ram if spare bytes exist in avalon buffer.
+- Remove off by one error.
+- Inverted logic.
+- Add more debugging to avalon reads.
+- Convert unsigned size_ts to ints for parsing avalon messages.
+- Cope with not finding nonces in avalon parsing gracefully by not overflowing
+buffers.
+- Adjust avalon temp values on one lot of valid nonces from the parser.
+- Created a threaded message parser for avalon reads.
+- Avalon_wait_write is not effective during resets so do it after going idle.
+- Send only a single byte reset.
+- Repeat going idle after avalon reset, and wait for write ready before sending
+each reset request instead of some arbitrary sleep time.
+- Timeouts on avalon_read and avalon_write should be 100ms.
+- Don't close avalon after detecting it until we're cleaning up, instead using
+reset for comms failures.
+- Check for avalon_wait_write before sending reset command.
+- Sleep in avalon_write_ready.
+- Make avalon_wait_write a bool function and check its return value.
+- Show how many idle tasks are sent to avalon if it aborts on buffer full.
+- Reset avalon->device_fd after it is closed.
+- Create an avalon_wait_write function that is used before sending avalon idle
+command.
+- Avoid repeating avalon_idle in do_avalon_close and extra sleep.
+- Pass fd to avalon_idle.
+- Do avalon_reset after info structure is set up.
+- Rework avalon reset sequence to include idling of chips and waiting for them
+to go idle followed by 2nd reset and then checking result.
+- Do a non-blocking read of anything in the avalon buffer after opening the
+device.
+- Assign the avalon info data to the device_data in cgpu_info.
+- thread shutdown is different on windows
+- usbutils make all windows timeouts 999ms
+- usb add another Cairnsmore1 USB chip
+- icarus do the full detect test twice if required
+- CMR usb config guess
+- usb add transfer_read and commented out in icarus
+- usbutils allow unrounded control transfers
+- icarus ICA initialisation
+- icarus report err on read failure
+- icarus correct device_id and use device_data for icarus_info
+- miner.h remove unused device_file and add device_data
+- miner.h icarus no long uses fd
+- icarus AMU config transfers
+- Create a logwin_update function which mandatorily updates the logwin and use
+it when input is expected to prevent display refresh delays.
+- usbutils force an unknown IDENT for zero
+- icarus set default options/timing based on device
+- Must unlock curses as well in logwin_update.
+- Create a logwin_update function which mandatorily updates the logwin and use
+it when input is expected to prevent display refresh delays.
+- icarus report usb write error information
+- Add name to icarus copyright notice.
+- Check for *pth dereference on pthread_join
+- usbutils name latency correctly
+- Check for restart before buffering more reads in Icarus.
+- Icarus should timeout if it's greater than the timeout duration even if it's
+receiving data.
+- We should check for amount buffered in icarus get_nonce against amount already
+received.
+- Make mining threads report out during work submission.
+- submit_work_async is no longer used directly by driver code.
+- Fix first read timeout on icarus get nonce.
+- Retry icarus_initialise if the first read attempt fails.
+- Properly pthread_join miner threads on shutdown.
+- Properly pthread_join miner threads on shutdown.
+- Use a persistent single separate thread for stratum share submission that uses
+workqueues since all stratum sends are serialised.
+- All stratum calls to recv_line are serialised from the one place so there is
+no need to use locking around recv().
+- Only allow the mining thread to be cancelled when it is not within driver
+code, making for cleaner shutdown and allowing us to pthread_join the miner
+threads on kill_work().
+- Only allow the mining thread to be cancelled when it is not within driver
+code, making for cleaner shutdown and allowing us to pthread_join the miner
+threads on kill_work().
+- Set pool->probed to true after an attempt to resolve the url via stratum code.
+- icarus test nodev everywhere
+- usbutils/icarus separate FTDI transfer values and more debug
+- add icarus to hotplug
+- usbutils add rest of icarus
+- simple serial-USB python test script
+- icarus->USB v0.1 incomplete - missing initialise()
+- README spelling
+- Update documentation for icarus switch to USB
+- Add USB rules for supported USB devices
+- switch icarus configuration to usb
+- usbutils new command for icarus
+- usb add a numeric sub-indentity for each name
+- usbutils - make FTDI handling automatic
+- fix duplicate name
+- usbutils set Black Arrow Lancelot's as BAL and match the lot->llt name
+- usbutils identify Icarus devices
+- libusb_control_transfer 16 bit words are endian specific.
+- usb_applog separate amt display
+- Show pool difficulty more verbosely if it changes via stratum.
+- Attribute whatever stats we can get on untracked stratum shares based on
+current pool diff.
+- Provide a --lowmem option which does not cache shares on failed submission to
+prevent low memory hardware (eg Avalon) from crashing.
+- Update util.c
+
+
+Version 3.1.1 - May 11th, 2013
+
+- Use a discrete device target for scrypt that dynamically changes to ensure we
+still report a work utility even if no shares are submitted such as in solo
+mining.
+- Make set_work_target a function to set a specified char as target for use
+elsewhere.
+- Further consolidate the hash regeneration between sha and scrypt doing it only
+once and always checking the share diff for both before submission.
+- Regenerate the hash before checking the share diff in hashtest().
+- Minor typo.
+- Use a scantime of 30 seconds for scrypt if none is specified.
+- Support more shares to be returned for scrypt mining.
+- Update the write config to properly record device entries and remove disabled
+option.
+- Show a different warning and loglevel for failure to resolve a URL on first or
+subsequent testing of stratum pool URLs.
+- Fix the problem of seting up termio of ttyUSB0 for icarus. the CSIZE  is the
+mask of CS2/4/8 From: navyxliu <navy.xliu@gmail.com>
+- Set all stratum sockets to nonblocking to avoid trying to use MSG_DONTWAIT on
+windows.
+- Fix warnings on win32 build.
+- Only use MSG_NOSIGNAL for !win32 since it doesn't exist on windows.
+- Use MSG_NOSIGNAL on stratum send()
+- Set TCP_NODELAY for !linux for raw sockets.
+- Use TCP_NODELAY with raw sockets if !opt_delaynet
+- Make raw sockets compile on windows
+- Recheck select succeeds on EWOULDBLOCK for stratum.
+- usbutils/mmq fixed size usb_read default to wait for all data
+- usbutils optional (disabled by default) dev debug
+- Add an ftdi usb read macro without newline
+- Avalon usb interface should be 0.
+- Add more debug for failure to USB init.
+- Recv() should all be non-blocking for raw sockets in stratum.
+- Change verbosity and error for getaddrinfo warnings in setup stratum socket.
+- Free servinfo after p is checked in setup stratum socket.
+- Use raw sockets without curl for stratum communications.
+- Sacrifice curl handle memory on stratum disconnects on all versions of libcurl
+to avoid curl corruption.
+- Don't use TCP_NODELAY if opt_delaynet is enabled with stratum.
+- Fix warnings in avalon driver.
+- Make FULLNONCE an ULL to fix a warning on 32 bit.
+- ztx correct applog typing
+- ocl correct applog typing
+- util correct applog typing
+- api correct applog typing
+- cgminer correct applog typing
+- scrypt correct applog typing
+- bfl correct applog typing
+- ica correct applog typing
+- mmq correct applog typing
+- adl fix trailing %
+- usbutils correct applog typing
+- applog - force type checking
+- Simplify the many lines passed as API data in the avalon driver now that the
+API does not need persistent storage for the name.
+- Duplicate the name string always in api_add_data_full to not need persistent
+storage for names passed to it.
+- Add extra matching work count data in API for Avalon with 4 modules.
+
+
+Version 3.1.0 - April 28th, 2013
+
+- va_copy is meant to be matched by a va_end in log_generic.
+- usbutils remove_in_use break
+- usbutils remove_in_use missing prev
+- usbutils missing add_in_use
+- Clean up summary slightly better on exit.
+- Make the scan sleep time after scanwork in bflsc dynamic to keep queues
+between watermark levels.
+- Remove unused temp counts in bflsc.
+- Calculate a rolling 5 min average set of temperatures for bflsc.
+- Damp the display of voltage for BFLSC devices.
+- Damp the temperature display measurement for bflsc since it fluctuates so
+wildly.
+- bflsc add volt stats
+- Handle failed tolines command in bflsc driver.
+- Can use a read lock instead of a write lock in bflsc scanwork.
+- Since we are filling a queue on the bflsc devices, there is no need to run
+through scanwork frequently provided we use the restart_wait function to abort
+early during a block change.
+- Remove flushed work in bfl scanwork from the hash table.
+- Set correct device in process_nonces in bflsc driver.
+- bflsc add work reply INPROCESS: missing from the spec
+- bflsc put in some error messages not yet written
+- bflsc get completed hashes as late as possible
+- Fix potential memory leak with unused work items in bflsc_queue_full
+- Reverse bools in bflsc_queue_full
+- Avoid recursive loop calling correct function instead.
+- bflsc fix details identification
+- Differentiate BFLSC device from regular bitforce and give warning if no
+support is compiled in.
+- util.c str_text make a fully text readable version of str
+- BFLSC fix FPGA identity overlap
+- Locking error in bflsc_send_work
+- Use htobe32 function for converting nonce in bflsc.
+- Replace deprecated bzero with memset in bflsc driver.
+- Fix compilation of bflsc driver without opencl.
+- Check for realloc failures in bflsc driver.
+- Check for failure to calloc in bflsc driver.
+- Trivial style change
+- Use copy_time function in bflsc driver.
+- Use cgtime in bflsc driver and update copyright notice.
+- Use a separate function for bfl initialise that doesn't require locking.
+- Fix BFLSC building.
+- bflsc v0.1
+
+
+Version 3.0.1 - April 25th, 2013
+
+- Bypass attempting to read and save binary files on OSX to avoid crashes on >1
+GPU.
+- Receive failures in recv_line should unconditionally fail.
+- Use sock_blocks in api.c
+- Use sock_blocks function for stratum send and receive.
+- Create an OS specific sock_blocks function.
+
+
+Version 3.0.0 - April 22nd, 2013
+
+- Further fix distdir for hexdump.c
+- Fix build and distdir.
+- Remove all CPU mining code.
+- compile on win32
+- Update SCRYPT README with improved hashrates for 7970.
+- Use copy_time helper throughout cgminer.c
+- Provide wrappers for commonly used timer routines with API stats.
+- Avoid one cgtime call in sole_hash_work.
+- Fulltest is true if value is <= target.
+- Use system host to endian functions for clarity in fulltest.
+- Provide endian_flipX functions to avoid special casing big endian in cgminer.c
+- Provide a flip128 helper to simplify big endian flipping.
+- Use flip helpers to simplify code for calculation of midstate.
+- Use flip32 function instead of open coding it in gen_stratum_work.
+- Move util.c exports to util.h
+- Fix warning on building avalon on win32
+- Use cgtime in driver-avalon.c
+- Use cgtime in driver-icarus.c
+- Use cgtime in driver-bitforce.c
+- Use cgtime in logging.c
+- Use cgtime in usbutils.c
+- Use cgtime in driver-opencl.c
+- Use cgtime wrapper in driver-modminer.c
+- Use cgtime in driver-ztex.c
+- Use cgtime in compat.h
+- Use cgtime instead of gettimeofday in fpgautils.c
+- Replace gettimeofday usage in cgminer.c with cgtime
+- Create a cgminer specific gettimeofday wrapper that is always called with tz
+set to NULL and increases the resolution on windows.
+- Add high resolution to nmsleep wrapper on windows.
+- Set default ocl work size for scrypt to 256.
+- define le32toh if needed
+- fliter out the wrong result from adjust fan code
+- compile avalon driver on win32 and win64
+- Restart threads on the rare chance we found the block ourselves.
+- Add more FAQs about crossfire.
+- Set last device valid work on adding device.
+- Increment last device valid work count in submit_nonce to cover scrypt.
+- Set opt_scrypt drv max diff for correctness.
+- Make scrypt submission use the submit_nonce code, with nonces matching
+endianness.
+- Do testing for HW errors on submit nonce for both scrypt and sha.
+- Increment hardware error count from the one site.
+- Rename scrypt regenhash function for consistency.
+- Add new best share info to verbose logging.
+- Add notice for when network diff is changed.
+- Convert error getting device IDs in ocl code to info log level only since
+multiple platforms may be installed and the error is harmless there.
+- Unnecessary extra array in ocl code.
+- Further driver FAQs.
+- Add MAC FAQ.
+- Add more FAQ details.
+- Check for work restart after disable in the hash queued work loop since it may
+be a long time before we re-enable a device.
+- Unconditionally test for many wrong results on avalon and reset to avoid
+passing a corrupt avalon result to temperature code.
+- build out of source dir
+- Set device_diff for queued work or there will be no diff1 share count.
+- Only reset an avalon device with no results when there are no results
+consecutively.
+- More FAQs.
+- More FAQs.
+- Cleanup when stratum curl fails to initialise.
+- Avoid applog in recalloc_sock.
+- Avoid applog under stratum_lock in recv_line.
+- Avoid applog under stratum_lock in __stratum_send.
+- Put spacing around locking in util.c for clarity.
+- Avoid applog under cg_wlock.
+- Put spacing around locking code for clarity.
+- Avoid applog under pool_lock.
+- Avoid more recursive locks.
+- Avoid applog while ch_lock is held.
+- Avoid recursive locks in fill_queue.
+- Variable is already initialised in global scope.
+- More GPU faqs.
+- More README faqs.
+- Yet more README faqs.
+- Add more faqs to README.
+- Wrap result wrong tests in avalon scanhash in unlikely() and only consider a
+hash count of zero wrong if a restart wasn't issued.
+- avalon: if result_wrong >= get_work_count jump out the read loop
+- Fix warning on 32bit.
+- Fix warning on 32bit.
+- Avoid curl_easy_cleanup on old curl versions in setup_stratum_curl as well.
+- fix the fan control on max temp2/3
+- for some reason network down. one simple cgminer command:   "cgminer -o
+127.0.0.1:8888 -O fa:ke --avalon-options 115200:32:10:50:256" can idle the
+avalon for safe power and protect chip
+- if hash_count == 0; reinit avalon, fix the 0MHS bug use the max value of temp1
+and temp2 for fan control
+- Reinstate the matching_work_count per subdevice on avalon based on the work
+subid.
+- Avalon driver is missing the drv_id.
+- Rationalise and simplify the share diff and block solve detection to a common
+site.
+- Rationalise and simplify the share diff and block solve detection to a common
+site.
+- Make the avalon array size a macro.
+- Use replacement of work items in the avalon buffer as needed instead of
+flushing them.
+- Reinstate wrong work count to reset avalon regardless and display number of
+wrong results.
+- Revert "The result_wrong measurement for avalon is continually leading to
+false positives so remove it."
+- select() on serial usb in avalon does not work properly with zero timeout.
+- The result_wrong measurement for avalon is continually leading to false
+positives so remove it.
+- Revert "Use only 2 queued work arrays in avalon."
+- Use no timeout on further reads in avalon_gets
+- Do sequential reads in avalon_get_reset to cope with partial reads.
+- Show read discrepancy in avalon_get_reset.
+- Reuse avalon_get_work_count variable.
+- Check for AVA_GETS_RESTART when deciding if avalon has messed up.
+- Make the detection of all wrong results on avalon much more conservative to
+avoid false positives on work restarts.
+- Show error codes on select and read fail in avalon.
+- If we get a restart message in avalon_gets still check if there's a receive
+message to parse first without a timeout before returning AVA_GETS_RESTART.
+- Use only 2 queued work arrays in avalon.
+- avalon_gets is always called from the one call site so inline it.
+- The read_count is unused by the avalon get result code and no longer required
+for avalon reset so simplify code removing it.
+- Use a separate avalon_get_reset function for resetting avalon instead of using
+avalon_get_result.
+- The current hash count returned by avalon scanhash is just an obfuscated
+utility counter so make it explicit.
+- Check for a restart before a timeout in message parsing code in avalon.
+- We should check for a restart message before checking for a timeout in avalon
+scanhash.
+- Store the subid for the work item in avalon.
+- usbutils more stats for bflsc
+- Fix record_temp_fan function in avalon driver. Patch by Xiangfu
+<xiangfu@openmobilefree.net>
+- Remove inappropriate memset of struct avalon result which was corrupting fan
+values.
+- Fix warning with no curses built in.
+- Bump version to 2.11.4
+- Add API support for Avalon.
+- Only do_avalon_close once on multiple errors.
+- Reset the result_wrong count on block change in avalon scanhash to prevent
+false positives for all nonces failed.
+- Small timeouts on select() instead of instant timeout increase reliability of
+socket reads and writes.
+- Only get extra work in fill_queue if we don't have any unqueued work in the
+list.
+- Small timeouts on select() instead of instant timeout increase reliability of
+socket reads and writes.
+- Rotate the avalon work array and free work on AVA_SEND_BUFFER_EMPTY as well.
+- Only get extra work in fill_queue if we don't have any unqueued work in the
+list.
+- Don't get any work if our queue is already full in avalon_fill.
+- Differentiate socket closed from socket error in recv_line.
+- Differentiate socket closed from socket error in recv_line.
+- Free avalon->works in the event we call avalon_prepare on failure to
+initialise.
+- Fix warnings.
+- Create an array of 4 lots of work for avalon and cycle through them.
+- Remove unused per unit matching work count for avalon.
+- Rename the confusing avalon_info pointer.
+- Simplify avalon scanhash code using the new find_queued_work_bymidstate
+function. Partially works only.
+- Members of cgpu_info for avalon are not meant to be in the union.
+- Use correct struct device_drv for avalon_drv.
+- cgminer.c -S help to only say Icarus
+- Check enough work is queued before queueing more in avalon_fill.
+- Actually put the work in the avalon queue.
+- Rneame avalon_api to avalon_drv.
+- First draft of port of avalon driver to new cgminer queued infrastructure.
+- Add Makefile entry for driver-avalon.
+- Add configure support for avalon.
+
+
+Version 2.11.4 - April 5th, 2013
+
+- Remove bfl-sc option  from configure for 2.11 branch.
+- Only update hashrate calculation with the log interval.
+- Update the total_tv_end only when we show the log to prevent failure to update
+logs.
+- Minor README updates.
+- Add example 7970 tuning for scrypt in readme.
+- Update driver recommendations.
+- Add extensive GPU FAQs for the flood of new Scrypt miners.
+- Remove help option for cpumining in build environment.
+- Remove scripts that make it too easy to compile CPU mining support.
+- Win32 and win64 build updates
+- Remove references to CPU mining from README.
+- Show share hash as little endian as needed.
+- usbutils extra message requirements
+- Make hashmeter frequency for hash_queued_work match sole_work.
+- Update links and recommended SDKs.
+- Update scrypt readme re drivers and sdk.
+- usbutils.c usb_cmdname() usb_cmds -> string name
+- BFL FPGA Windows timeout set to 999ms
+- AUTHORS - spam update time (one year since the last)
+- Update README for x970 memdiff values.
+- Update README to match changes to display.
+- Remove increasingly irrelevant discarded work from status lines.
+- Remove increasingly irrelevant queued and efficiency values from status and
+move WU to status line.
+- Allow cgminer to start if usb hotplug is enabled but no devices yet exist.
+- Do not scan other gpu platforms if one is specified.
+- Update README for sync objects on windows.
+- Update README about intensity.
+- Add information for setting gpu max alloc and sync parameters for windows with
+scrypt.
+- If the hashmeter is less than the log interval and being updated by the
+watchdog, don't update the hashrate.
+
+
+Version 2.11.3 - March 17, 2013
+
+- Update docs and reorder README to show executive summary near top.
+- Update the hashmeter at most 5 times per second.
+- Usbutils use its own internal read buffer
+- Calculate work utility for devices that support target diffs of greater than
+1, and update scrypt code to use it.
+- usbutils allow read termination match to be a string
+- Set default GPU threads to 1 for scrypt.
+- Connect backup stratum pools if the primary pool cannot deliver work.
+- Use a new algorithm for choosing a thread concurrency when none or no shader
+value is specified for scrypt.
+- Do not round up the bufsize to the maximum allocable with scrypt.
+- Remove the rounding-up of the scrypt padbuffer which was not effectual and
+counter-productive on devices with lots of ram, limiting thread concurrencies
+and intensities.
+- bufsize is an unsigned integer, make it so for debug.
+- Update the hashmeter once per second but only display the extra logs every
+opt_log_inteval.
+- add a dummy ztex to usbutils so cgminer -n lists ztex also
+- nDevs required for -n with usb
+- USB device list - convert some common error numbers to messages
+- USB -n 'known' text only without ---usb-list-all
+- USB modify -n and --usb-dump to only show known devices or use new
+--usb-list-all option to see all
+- Make pool adding while running asynchronous, using the pool test thread
+functionality.
+- Only curl easy cleanup a stratum curl if it exists.
+- Sacrifice the ram of curl handles in stratum disconnects when we have built
+with old libcurl to avoid crashes.
+- cgminer -n to include a USB device list
+- usbutils allow call of usb_all() from other code
+- Convert gbt_lock to a cg_lock.
+- Add intermediate variants of cglocks that can be up or downgraded to read or
+write locks and use them for stratum work generation.
+- Move the stratum and GBT data to be protected under a new cg_lock data_lock.
+- Convert the ch_lock to cg_lock.
+- Convert the control_lock to a cg_lock.
+- Remove unused qd_lock.
+- Implement cg_lock write biased rwlocks.
+- do usb_initialise() after the started message so we see it
+- --usb-dump display brief dump if value = 0
+- USB add --usb options to limit USB device selection v0.1
+
+
+Version 2.11.2 - March 9, 2013
+
+- Whitelist AMD APP SDK 2.8 for diablo kernel.
+- Cope with the highest opencl platform not having usable devices.
+- Fix memory leak with share submission on GPU work structures as discovered by
+twobitcoins.
+- usb_cleanup() without locking.
+- Use curl_easy_cleanup to close any open stratum sockets.
+- Show pool number in switch message
+- Don't start testing any pools with the watchpool thread if any of the test
+threads are still active.
+- Set sockd to false should curl setup fail on stratum.
+- Close any open sockets when reusing a curl handle and reopen the socket
+whenever we're retrying stratum.
+- Set pool died on failed testing to allow idle flag and time to be set.
+- Remove unused pthread_t typedefs from struct pool.
+- Perform pool_resus on all pools that are found alive with the test pool
+threads.
+- Use pool_unworkable in select_balanced as well.
+- Differentiate pool_unusable from pool_unworkable.
+- Keep a connection open on higher priority stratum pools to fail back to them.
+- Rename threads according to what pool they're associated with as well.
+- Set the wrong bool in pool_active
+- Start the stratum thread only if we successfully init and authorise it,
+otherwise unset the init flag.
+- Make the initialisation of the stratum thread more robust allowing the
+watchpool thread safe access to it after the stratum thread is started.
+- API no longer ignore send() status
+- API make the main socket non-static
+
+
+Version 2.11.1 - March 7, 2013
+
+- Shorten the time before keepalive probes are sent out and how frequently
+they're sent with stratum curls.
+- Only set stratum auth once to prevent multiple threads being started.
+- Display select return value on select fail in stratum thread.
+- Clear the socket of anything in the receive buffer if we're going to retry
+connecting.
+- Allow pools to be resuscitated on first startup by the watchpool thread.
+- Check all pools simultaneously at startup switching to the first alive one to
+speed up startup.
+- Clear just the socket buffer when we don't care what is left in a stratum
+socket.
+- Clear the stratum socket whenever we are closing it since the buffer is going
+to be reused.
+- Do not continue work from a stratum pool where the connection has been
+interrupted.
+- Reset stratum_notify flag on suspend_stratum as well.
+- Close any sockets opened if we fail to initiate stratum but have opened the
+socket.
+- Close any existing stratum socket if we are attempting to restart stratum so
+the pool knows the connection has gone.
+- Show mechanism of stratum interruption if select times out.
+- Make stratum connection interrupted message higher priority to be visible at
+normal logging levels.
+- Implement client.show_message support for stratum.
+- API add 'Network Difficulty' to 'coin'
+- Setup BFLSC support
+- API use control_lock when switching pools
+- Make sure to retry only once with noresume support for stratum.
+- Instead of keeping track of when the last work item was generated to keep
+stratum connections open, keep them open if any shares have been submitted
+awaiting a response.
+- usbutils.c copy full size to 'Last Command'
+- configure - set USE_USBUTILS when usbutils is required and use it in the code
+- Clear last pool work on switching pools if the current pool supports local
+work generation or we are in failover only mode.
+- make rw locks: mining_thr_lock and devices_lock
+- Release MMQ device only once (not 4 times)
+- api.c fix MSG overlap
+- Hotplug - allow setting interval via --hotplug or API
+- curses - fix - put a dev_width inside #ifdef
+- usb_cleanup() use correct locking mechanism
+- Implement and use usb_cleanup() on shutdown or restart
+- miner.php report 'Last Valid Work' as time before request
+- API - return Last Valid Work
+- api -> drv
+- ZTX bug set missing drv_id
+
+
+Version 2.11.0 - March 2, 2013
+
+- Update kernel file names signifying changes.
+- Update a pool's last work time when the work is popped as well as staged.
+- API always report failed send() replies
+- Update diff stale: total and pools when stratum throws away shares
+- Keep stratum connections open for 2 minutes after the last work item was
+staged to allow stray shares to be submitted on pool switching.
+- Try to extract the sessionid associated with mining.notify on 3rd level array
+and submit it along with the userid to support mining resume, failing gracefully
+and restarting if the pool rejects it.
+- Speed up watchdog interval and therefore display updates to 2 seconds.
+- Update copyright dates.
+- Cope with misread sessionid on stratum for now.
+- Use constants from the array of __constants throughout the diablo kernel.
+- Create a __constant array for use within diablo kernel.
+- Fix --benchmark generating valid work for cgminer.
+- Use the sessionid as passed on stratum connect to attempt to resume a
+connection once and then clear it if it fails, to use a new connection.
+- Move to storing the nonce1 in the work struct instead of the sessionid for the
+now defunct first draft mining.resume protocol.
+- Use global constant arrays for all other constants used in scrypt kernel.
+- Use global __constants for sha functions in scrypt kernel.
+- Use constants for endian swap macros.
+- Revise scrypt kernel copyright notice.
+- Separate out additions in scrypt kernel.
+- Reuse some Vals[] variables that can be assigned to constants earlier in the
+poclbm kernel, making for fewer ops.
+- Put all constants used in poclbm kernel into __const memory array to speed up
+concurrent reads on the wavefront.
+- BFL stop 1st init command if no device
+- Add a get_queued function for devices to use to retrieve work items from the
+queued hashtable.
+- Bugfix: Duplicate stratum sessionid when copying work, to avoid double-free
+- Bugfix: Missing pool_no parameter to applog for no-stratum-sessionid debug
+message
+- Add the choice of hash loop to the device driver, defaulting to hash_sole_work
+if none is specified.
+- Add comments.
+- Add a driver specific flush_work for queued devices that may have work items
+already queued to abort working on them on the device and discard them.
+- Flush queued work on a restart from the hash database and discard the work
+structs.
+- Create a central point for removal of work items completed by queued device
+drivers.
+- Create a fill_queue function that creates hashtables of as many work items as
+is required by the device driver till it flags the queue full.
+- Create the hash queued work variant for use with devices that are fast enough
+to require a queue.
+- Update copyright year.
+- Fix tv_lastupdate being made into tv_end and update the hashmeter on cycle,
+not opt_log_interval.
+- Fix tv_lastupdate being made into tv_end and update the hashmeter on cycle,
+not opt_log_interval.
+- Only continue submitting shares with mining.resume support on stratum when the
+session id matches.
+- Provide support for mining.resume with stratum, currently re-authorising after
+successful resumption pending finalising of the protocol process.
+- Provide basic framework for restarting stratum depending on whether resume
+support exists or not.
+- Abstract out the setting up of the stratum curl socket.
+- Free sessionid in clean_work and remove redundant setting of strings to NULL
+since the whole work struct is zeroed.
+- Only clear stratum shares mandatorily on stratum dropouts when the pool does
+not support resume.
+- Try resubmitting stratum shares every 5 seconds for up to 2 minutes if the
+pool session id exists and matches on failure to submit.
+- Do as much outside of mutex locking of sshare_lock as possible.
+- Remove last reference to struct work used outside the sshare_lock in
+submit_work_thread
+- Unlock the sshare_lock in submit_work_thread when all references to work and
+sshare are complete.
+- Add timestamps to stratum_share structs as they're generated and copy the
+stratum sessionid if it exists to stratum work generated.
+- Store session id for stratum if the pool supports it for future mining.resume
+support.
+- API.java allow partial reads
+- debug_cb buffer type warning
+- MMQ rewrite the last of the old scanhash loop and drastically reduce CPU
+- hash_sole_work can be static
+- Make the numbuf larger to accept larger scrypt parameters.
+- Keep the unique id of each work item across copy_work to prevent multiple work
+items having the same id.
+- Abstract out the main hashing loop to allow us to use a separate loop for
+devices that are fast enough to require queued work.
+- Provide a noop thread_enable function for drivers that don't support it.
+- Provide a noop thread_shutdown function for drivers that don't support it.
+- Provide a noop hw_error function for drivers that don't support it.
+- Provide a noop prepare_work for drivers that don't support it.
+- Provide a noop thread_init for drivers that don't support it.
+- Provide a noop can_limit_work for devices that don't support it.
+- Provide a noop thread_prepare function for drivers that don't use
+thread_prepare.
+- Use blank_get_statline_before for GPU devices that don't support adl
+monitoring.
+- Provide a noop get_stats function for drivers that don't support it.
+- Provide a blank get_statline for drivers that don't support it.
+- Provide a blank get_statline_before function for drivers that don't have one.
+- Fill drivers missing reinit_device with a noop version.
+- add 'count' to cumstomsummarypage 'calc'
+- hotplug use get_thread() where appropriate
+- convert sleep(const) to nmsleep()
+- remove empty #ifdef
+- call a separate get_devices() with locking, as required
+- usbutils - avoid free cgusb twice
+- usbutils hotplug v0.1
+- Report USB nodev as ZOMBIE on the screen
+- Change file modes.
+
+
+Version 2.10.5 - February 7, 2013
+
+- Fix logic fail on partial writes with stratum send that was leading to corrupt
+message submissions.
+- Do not consider every call to stratum_resumed a pool recovery unless it was
+actually idle.
+- Do not enable the pool disable on reject feature unless explicitly enabled
+with --disable-rejecting.
+- Stratum disconnect shares - count total against stale
+- Use sanity checking to prevent a possible overflow with invalid data being
+given by the pool for difficulty as reported by luke-Jr.
+- Check for calloc failure for completeness in gen_stratum_work.
+- Cache the coinbase length to speed up stratum work generation.
+- Cache the header length when generating stratum work to avoid calculating it
+on every work generation, and to only need one alloc+sprintf, speeding up work
+generation.
+- Use heap ram for coinbase in gen_stratum_work, zeroing it before use.
+- Provide a wrapper for aligning lengths of size_t to 4 byte boundaries.
+- Fix memory leak on stratum share submission.
+- Zero the best share string memory when zeroing stats.
+
+
+Version 2.10.4 - December 29, 2012
+
+- Change the pool stratum socket buffer to be dynamically allocated to
+accomodate any size coinbase and keep receiving data in recv line for up to 60s
+if no end of line has been received.
+- Differentiate socket full from sock full.
+- Allow stratum to startup without notify but check it is valid before creating
+stratum work.
+- Do not try to generate stratum work unless the notify command has succeeded.
+- Reset total diff1 shares when zeroing stats as well to show correct work
+utility.
+
+
+Version 2.10.3 - December 26, 2012
+
+- Do not give the share submission failure message on planned stratum
+disconnects.
+- Parse anything in the stratum socket if it's full without waiting. Empty the
+socket even if a connection is not needed in case there are share returns.
+- Provide a mechanism to zero all the statistics from the menu.
+- Display the current pool diff in the status line.
+- Display block diff in status line.
+- Generalise the code for solving a block to enable block solve detection with
+scrypt mining.
+- Generate the output hash for scrypt as well and use the one function to set
+share_diff.
+- Use the flip80 function in regeneratehash and the correct sized hash array.
+- Use one size for scratchbuf as a macro in scrypt.c
+- Stage work outside of the stgd lock to prevent attempted recursive locking in
+clone_available.
+- share_diff needs to be performed on a BE version of the output hash to work,
+leading to false best_share values as spotted by luke-Jr.
+- Remove the unused sha224 functions.
+- Use the flip functions in hashtest.
+- Simplify the setting of the nonce data field in work on submitting nonces.
+- Scrypt code does not enter the hashtest function.
+- Go back to cloning available work under staged lock.
+- Updated links to AMD APP SDK
+- Updated link to ADL SDK
+- scrypt_diff uses a uint64_t as well.
+- Correct target for stratum support with scrypt mining.
+- libztex: fixed a typo
+- libztex: check returnvalue of libusb_claim_interface() and release the
+interface in case of early exit
+
+
+Version 2.10.2 - December 19, 2012
+
+- Stop all work from the current pool if it's a stratum pool once it is
+disconnected since it will be invalid upon reconnecting.
+- Discard all staged work from stratum pools as well as the shares upon
+disconnection since all the work becomes invalid.
+- Use correct cbreak after 15 second delay when no pool is found alive.
+- MMQ missing firmware -> ERR not DEBUG
+- Allow stratum to work with scrypt.
+- MMQ ensure delta clock can never exceed limits
+- MMQ lowercase new string constants
+- MMQ add api pgaset for clock
+- API V1.23 - new pgaset command, to be used soon
+- Protect the best_share/best_diff values under control lock.
+- MMQ style police
+- MMQ count work check timeout failures
+- MMQ allow partial work replies and count them
+- Check a stratum pool hasn't gone dead while being a backup pool and missed
+having its idle flag cleared.
+- MMQ overheat: remove clockdown (doesn't help) + ensure no lost shares
+- API-README grammar
+- API-README explain custom page extensions in miner.php
+- miner.php add a sample group pool report
+- miner.php allow where,group,having on cumstom pages
+
+
+Version 2.10.1 - December 14, 2012
+
+- Check for EWOULDBLOCK when supported in send and recv as well.
+- Use the raw send() command instead of curl_easy_send since curl raw socket
+usage introduces random bugs on windows.
+- Use raw recv() command in place of curl_easy_recv since the curl
+implementation introduces random bugs on windows builds when the recv fails.
+- miner.php when displaying a single rig, add prev/next rig buttons if they
+exist, next to refresh
+- miner.php allow custom page joins for STATS
+- API show if pool has GBT (so people know not to use that pool)
+- miner.php - include windows easyphp link
+- driver-ztex: use the correct size for the swap array
+- API stats - display pool byte transfer stats
+- Pool store data transfer stats
+- README ModMiner dependency
+- Benchmark incorrect work size
+- ChangeLog refer to NEWS
+- MMQ handle over temp differently and hash longer
+- driver-ztex: search the complete noncerange based on the actual speed
+- README - update ModMiner details
+- API-README update
+- api use a dynamic io buffer, truncated before it reaches the current ~64k
+limit
+
+
+Version 2.10.0 - December 10, 2012
+
+- Include prctl header for thread renaming to work.
+- Set tv_idle time if a pool is not active when input from the menu.
+- usb display message when device is in use/another cgminer
+- libztex: avoid the use of libusb_error_name()
+- minor unlikely zero pointer test
+- BeaverCreek doesn't like BFI INT patching.
+- Only stratum pools that are idle need to be kicked via cnx_needed.
+- mmq - abbreviate the temperature numbers
+- Do not do any setup if opt_api_listen is disabled in api.c.
+- usbutils.c uninitialised usbstat for non-primary mmqs
+- Only set the lagging flag for select_pool() on failed getwork if we're not in
+opt_fail_only mode.
+- libztex: in case the selectFpga() failed set the selected fpga to unknown
+- Modified windows-build.txt to update git instructions.
+- libztex: use a function for the twice called firmware reset code
+- libztex: removed an unused struct member (ztex->valid)
+- driver-ztex: support for broken fpga on a multifpga board
+- Set the pool lagging flag on startup to avoid it being shown initially, and
+only unset it once the maximum number of staged work items has been reached.
+- Avoid recursive locking of the stgd lock.
+- Return value of keep_sockalive is no longer used.
+- Remove dependency on mstcpip.h for windows build by making curl version >=
+7.25.0 mandatory on windows builds, and use curl functions for keepalive
+whenever possible instead.
+- Make main() the getwork scheduler once everything is set up, so that all app
+exits use the kill_work and quit paths.
+- ztex: more style and whitespace fixes
+- libztex: silenced another warning
+- Set successful connect to true on auth stratum to allow summary on exit from
+single stratum pool.
+- Only consider work stale for stratum of different job_id if it's not a share.
+- Increment version preempting changed version signifying different codebase to
+2.9
+- Hash_pop should signal further waiters on its own pthread conditional in case
+there are multiple waiters.
+- Check the job_id has not changed on stratum work when deciding if the work is
+stale as might occur across disconnections.
+- Perform pool_resus on getwork pool that generates work in getwork_thread.
+- Set pool lagging message for getwork pool that falls to zero staged in getwork
+thread.
+- Stage extra work when the primary pool is a getwork pool without rolltime.
+- Do not try to clean up twice if kill message is given.
+- Only recalculate total_staged in getwork thread if required.
+- Include the correct config header in libztex and include it before other
+includes.
+- Implement a completely new getwork scheduler. Stage all work from the one
+thread, making it possible to serialise all requests minimising the number of
+getworks requested or local work generated. Use a pthread conditional to wake up
+the thread whenever work is removed to generate enough work to stay above the
+watermark set by opt_queue. Remove all remnants of the old queueing mechanism,
+deleting the now defunct queued count.
+- libztex: fixed some warnings and removed some whitespaces
+- libztex: silenced some warnings
+- Remove all references to the now unused workio_cmd structure.
+- Remove the old workio command queue thread, replacing it with a kill
+conditional to exit the program.
+- Remove getwork command from workio_cmd queues and do them directly from
+queue_request.
+- Begin tearing down the old workio command queues by removing submit commands
+from there and submit them asynchronously via their own threads.
+- Update windows build instructions.
+- Set pool probed to true on successful authorisation with stratum to avoid it
+being pinged later with pool_getswork.
+- driver-ztex: libztex_setFreq() must be called before ztex_releaseFpga()
+- driver-ztex: changed two pairs of malloc()/memset() to calloc()
+- libztex: Read bitstream file in 2kb blocks with simpler and faster code
+- Added the binary versions of ztex_ufm1_15d4.ihx and ztex_ufm1_15y1.ihx
+- Trivial space removal.
+- libztex: Add firmware download support for ZTEX 1.15d and 1.15x
+- libztex: Factor out local version of libusb_get_string_descriptor_ascii()
+- Shut up some boring old cpu warnings.
+- Style changes.
+- Allow pool active to be called on stratum or disabled pools in the watchpool
+thread if the pool has not been probed.
+- libztex: Make log messages say bitstream when refering to bitstreams
+- libztex: Don't return error when a bitstream was already configured
+- libztex: Read bitstream file in 64kb blocks with simpler and faster code
+- libztex: Verify that the mining firmware is not a dummy firmware
+- libztex: Match mining firmware ZTEX descriptor against the dummy firmware
+- Combine shared padding into one char.
+- libztex: Start download sequence only after reading in the new firmware
+- libztex: Download mining firmware to all devices with dummy firmware
+- lock (most of) the threaded statistics updates
+- README stats don't add up
+- usbutils.c remove compiler warning
+- Make need connection return true if a pool is idle.
+- API add Best Share to summary
+- Check on creating new GBT work if the structures are up to date and update
+them as required rather than regularly.
+- Update windows build instructions.
+- Enable backup stratum connections for getwork when the primary pool doesn't
+have longpoll aka solo mining.
+- Check for correct absence of opt_fail_only in cnx_needed.
+- Remove unused variable.
+- The specification for stratum has been elaborated to say that a changed diff
+applies only to new work so do not retarget when submitting shares.
+- Use a variable length string array in submit_upstream_work to cope with
+massive GBT submissions.
+- API lock access to some summary statistics (and copy them)
+- Suspend stratum connections to backup pools when there is no requirement to
+potentially grab work from them.
+- Fix missing export for RenameThread.
+- enumerate the mining threadnames
+- MMQ avoid possible number overrun crashes
+- mmq usb v0.4 + api usb stats
+- setting the name of the threads for linux,freebsd,openbsd and osx code is
+borrowed from bitcoins util.c, so it is already tested
+- Don't show broken WU value with scrypt mining.
+- Style police.
+- Remove unused getwork times in getswork.
+- Fix readme wordwrap.
+
+
+Version 2.9.6 - December 2, 2012
+
+- Make gen_stratum_work more robust by using a dynamically allocated array for
+the header in case bogus data is sent by the pool to avoid overflowing a static
+array.
+- scrypt_diff now returns a uint64_t
+- Support monitoring and reporting much higher diffs for scrypt mining,
+truncating irrelevant zeroes from displayed hash.
+- Pass ostate values around in scrypt to be able to extract full hashes if
+needed later on.
+- Since we will be using calloc_str to put a string into it, convert the
+function to calloc_strcat which does it automatically.
+- Revert "Handle crash exceptions by trying to restart cgminer unless the
+--no-restart option is used."
+- Count longpoll and GBT decodes as queued work since the count otherwise
+remains static.
+- Use the string helper functions to create gbt blocks of any length.
+- Provide helper functions calloc_str and realloc_strcat to create and extend
+arbitrary length arrays based on string length.
+
+
+Version 2.9.5 - November 25, 2012
+
+- fixes target calc for mips openwrt
+- openwrt needs roundl
+- Get rid of unused last_work in opencl thread data.
+- Do away with the flaky free_work api in the driver code which would often lose
+the work data in opencl and simply flush it before exiting the opencl scanhash.
+- Use base_work for comparison just for cleanness in __copy_work
+- Remove all static work structs, using the make and free functions.
+- Add pool no. to stale share detected message.
+- Add info about which pool share became stale while resubmitting.
+-b Copy the work on opencl_free_work
+- Add an extra slot in the max backlog for ztex to minimise dupes.
+- Do not use or count the getworks submitted which are simply testing that pools
+are still up. This was increasing share leakage and making stats not reflect
+real work.
+- Track all dynamically allocated memory within the work struct by copying work
+structs in a common place, creating freshly allocated heap ram for all arrays
+within the copied struct. Clear all work structs from the same place to ensure
+memory does not leak from arrays within the struct. Convert the gbt coinbase and
+stratum strings within the work struct to heap ram. This will allow arbitrary
+lengths without an upper limit for the strings, preventing the overflows that
+happen with GBT.
+- libztex: Work around ZTEX USB firmware bug exposed by the FreeBSD libusb
+- opencl: Use new dev_error function for REASON_DEV_NOSTART
+
+
+Version 2.9.4 - November 18, 2012
+
+- Provide rudimentary support for the balancing failover strategies with stratum
+and GBT by switching pools silently on getwork requests.
+- Convert remaining modminer and bfl uses of usleep to nmsleep.
+- Convert libztex to nmsleep where possible.
+- Convert unreliable usleep calls to nmsleep calls in ztex driver.
+- Support workid for block submission on GBT pools that use it.
+- Provide rudimentary support for literal ipv6 addresses when parsing stratum
+URLs.
+- Work around libcurl cflags not working on hacked up mingw installations on
+windows.
+- Only increase gpu engine speed by a larger step if the temperature is below
+hysteresis instead of increasing it to max speed.
+- Convert pool not responding and pool alive message on backup pools to verbose
+level only since they mean a single failed getwork.
+- Update work block on the longpoll work item before calling restart threads to
+ensure all work but the longpoll work item gets discarded when we call
+discard_stale from restart_threads.
+- Do not attempt to remove the stratum share hash after unsuccessful submission
+since it may already be removed by clear_stratum_shares.
+- Check against a double for current pool diff.
+- Support for fractional diffs and the classic just-below-1 share all FFs diff
+target.
+
+
+Version 2.9.3 - November 11, 2012
+
+- Make header larger on gen stratum work to accomodate \0 at the end.
+
+
+Version 2.9.2 - November 11, 2012
+
+- Use stratum block change from backup pools as an alternative to longpoll for
+pools that don't support LP.
+- Check share target diff for best_share to be calculated when solo mining.
+- Round some more static string arrays to 4 byte boundaries.
+- There is no need for the static arrays to be larger than required, so long as
+they're 4 byte aligned to appease ARM.
+- Store the full stratum url information in rpc_url for correct configuration
+file saving.
+- Put in a hack to prevent dud work from sneaking into test_work_current being
+seen as a new block.
+- Reset the work->longpoll flag where it will affect stratum work items as well.
+- Check for both coinbase/append and submit/coinbase support before using GBT
+protocol.
+- First pass through testing for GBT should not set probed to true since we are
+about to probe again.
+- Hash1 is only used by the deprecated cpu mining code and never changes so
+remove it from the work struct and bypass needing to process the value for all
+other mining.
+- Get a work item once per minute for all getwork and GBT pools to test they're
+still alive and to maintain a current GBT template.
+- Get a fresh block template with GBT pools on switching to them.
+
+
+Version 2.9.1 - November 6, 2012
+
+- Reset work flags to prevent GBT shares from being submitted as stratum ones
+after switching.
+
+
+Version 2.9.0 - November 6, 2012
+
+- Add endian swap defines for where missing.
+- Only retarget stratum shares to new pool diff if diff has dropped.
+- Remove resetting of probed variable when detecting GBT.
+- Count lost stratum share submits and increase message priority to warning.
+- Only retrieve a new block template for GBT pools that are the current pool.
+- Show which pool untracked share messages have come from.
+- Add management for dead GBT pools.
+- Count lost shares with stratum as submit stale lost.
+- Discard record of stratum shares sent and report lost shares on disconnection
+since they will never be reported back.
+- Swab, don't just swap the bytes in the GBT target.
+- Change status window message for GBT connected pools versus LP.
+- Generate a gbt work item from longpoll when required to set new block and
+message appropriately.
+- Use existing pool submit_old bool from gbt data.
+- Retrieve a new block template if more than 30 seconds has elapsed since the
+last one to keep the data current and test the pool is still alive.
+- Update GBT longpollid every time we request a new longpoll.
+- Manage appropriate response codes for share submission with GBT.
+- Allow the longpoll thread to start with GBT and only set the longpollid once.
+- Correct last few components of GBT block generation courtesy of Luke-jr.
+- Use correct length for offsetting extra nonce and remaining data.
+- Flip all 80 bytes in the flip function which was wrongly named flip256 for its
+purpose.
+- Calculate midstate for gbt work and remove now unused variable.
+- Use a standard function for flipping bytes.
+- Insert the extra nonce and remaining data in the correct position in the
+coinbase.
+- Remove txn size debugging and enlarge gbt block string to prevent overflow.
+- Remove varint display debugging.
+- Build varint correctly for share submission and sleep 5 seconds before
+retrying submit.
+- Make gbt_coinbase large enough for submissions, swap bytes correctly to make a
+header from GBT and encode the number of transactions in share submission.
+- Store the fixed size entries as static variables in GBT in binary form,
+byteswapping as is required.
+- 32 bit hex encoded variables should be in LE with GBT.
+- Target and prevblockhash need to be reversed from GBT variables.
+- Construct block for submission when using GBT.
+- Use same string for debug as for submission and make string larger to cope
+with future GBT messages.
+- Skip trying to decipher LP url if we have GBT support.
+- Store all the transaction hashes in pool->txn_hashes instead of separating
+txn0 and correct generation of merkle root, fixing memory overwrites.
+- Hook into various places to generate GBT work where appropriate.
+- Create extra work fields when generating GBT work.
+- Generate header from correct hashing generation of the merkle root for GBT.
+- Generate the merkle root for gbt work generation.
+- Create a store of the transactions with GBT in the minimum size form required
+to generate work items with a varied coinbase.
+- Create a function that generates a GBT coinbase from the existing pool
+variables.
+- Extract and store the various variables GBT uses when decoding gbt work.
+- Check for invalid json result in work_decode.
+- Decode work in separate functions for getwork vs gbt.
+- Check for the coinbase/append mutable in GBT support to decide whether to use
+it or not.
+- Add a gbt mutex within the pool struct for protecting the gbt values.
+- Convert work decode function to prepare for decoding block templates.
+- Check for GBT support on first probing the pool and convert to using the GBT
+request as the rpc request for that pool.
+- Make the rpc request used with getwork a pool variable to allow it to be
+converted to/from gbt requests.
+- Changes to build prototypes to support building on FreeBSD 9.1-RC2 amd64
+- Free old stratum_work data before replacing it
+- There is no need for addrinfo any more.
+- server and client sockaddr_in are no longer used in struct pool.
+- Merge pull request #322 from luke-jr/bugfix_stratum_tmpwork
+- Set sshare id and swork_id within the sshare mutex to avoid multiple share
+submits with the same id.
+- Initialize temporary stratum work
+
+
+Version 2.8.7 - October 29, 2012
+
+- Fail on select() failing in stratum thread without needing to attempt
+recv_line.
+- Add share to stratum database before sending it again in case we get a
+response from the pool before it's added.
+
+
+Version 2.8.6 - October 29, 2012
+
+- Shorten the initiate stratum connect timeout to 30 seconds.
+- Shorten the stratum timeout on read to 90 seconds to detect unresponsive pool.
+- Display best share difficulty on exit.
+- Make stratum socket fail more robust on windows by disabling the send buffer.
+- Reuse the same curl handle forcing a new connection instead of risking
+derefencing.
+- Add information about submission failure to stratum send.
+- Only add stratum share to database if we succeeded in submitting it, with a
+debug output saying it succeeded.
+- Use keepalive with stratum sockets to improve its ability to detect broken
+connections.
+- Show only the URL in the status bar to avoid long prefixes making for extra
+long lines.
+- Display compact status in menu and update README to reflect current menu
+entries.
+- Add a compact display mode that does not list per device statistics in the
+status window.
+- Add blank spaces after best share displayed.
+- Round a few static string arrays up to 4 byte boundaries for ARM.
+- Display best share diff for scrypt as well.
+- Show the best diff share as "best share" and add info to the README.
+- Display the best diff share submitted so far.
+- Redundant check.
+- The work struct pointer in struct pc_data in findnonce is never freed yet
+there is no need to allocate it separately so make struct work a static part of
+the struct pc_data. s
+
+
+Version 2.8.5 - October 23, 2012
+
+- Handle crash exceptions by trying to restart cgminer unless the --no-restart
+option is used.
+- Switch queued count when choosing a different pool from a failed stratum pool
+in getwork thread.
+- Put a mandatory 5s wait between reattempting a getwork on failure to avoid
+hammering requests.
+- The ATI stream / AMD APP SDK environment variables appear to only interfere
+with win32 builds so bypass them.
+- Make sure to check pool stratum curl exists under lock before attempting any
+recv to not risk dereferencing upon attempting to reinitiate stratum.
+- Avoid redefining macros and align to 4 byte boundaries.
+- API - add Stratum information to pools
+- update FPGA-README for MMQ
+
+
+Version 2.8.4 - October 18, 2012
+
+- Time for dynamic is in microseconds, not ms.
+- x86_64 builds of mingw32 are not supported directly and should just configure
+as generic mingw32 builds since they're NOT 64 bit.
+- Cope with both ATI stream and AMD APP SDK roots being set when building.
+- Use 3 significant digits when suffix string is used and values are >1000.
+- MMQ new initialisation (that works) and clocking control
+- Get rid of unused warning for !scrypt.
+- Use select on stratum send to make sure the socket is writeable.
+- Cope with dval being zero in suffix_string and display a single decimal place
+when significant digits is not specified but the value is greater than 1000.
+- Pad out the suffix string function with zeroes on the right.
+- Failure to calloc in bin2hex is a fatal failure always so just check for that
+failure within the function and abort, simplifying the rest of the code.
+- Provide locking around the change of the stratum curl structures to avoid
+possible races.
+- Bump opencl kernel version numbers.
+- Remove atomic ops from opencl kernels given rarity of more than once nonce on
+the same wavefront and the potential increased ramspeed requirements to use the
+atomics.
+- Clear the pool idle flag in stratum when it comes back to life.
+- Display correct share hash and share difficulty with scrypt mining.
+- Use explicit host to BE functions in scrypt code instead of hard coding
+byteswap everywhere.
+- Show work target diff for scrypt mining.
+- Ease the checking on allocation of padbuffer8 in the hope it works partially
+anyway on an apparently failed call.
+- Watch for buffer overflows on receiving data into the socket buffer.
+- Round target difficulties down to be in keeping with the rounding of detected
+share difficulties.
+- Dramatically simplify the dynamic intensity calculation by oversampling many
+runs through the opencl kernel till we're likely well within the timer
+resolution on windows.
+- String alignment to 4 byte boundaries and optimisations for bin<->hex
+conversions.
+- In opencl_free_work, make sure to still flush results in dynamic mode.
+- Align static arrays to 4 byte boundaries to appease ARM builds for stratum.
+
+
+Version 2.8.3 - October 12, 2012
+
+- Left align values that are suffix_string generated.
+- Share_diff should not be converting the work data to hex.
+- Off by one error.
+- Prevent overflows of the port char array in extract_sockaddr.
+- Disable stratum detection with scrypt.
+- Use the suffix string function when displaying device hashrates.
+- Be consistent with the get_statline function.
+- Use the suffix string function for displaying hashrate with 4 significant
+digits.
+- Display the actual share diff next to the pool required diff, using a suffix
+creation function to prevent values of >1000 being shown in their entirety.
+- Fix 4 * 0 being 0 that would break dynamic intensity mode.
+- Fix wrong byteswap macro being used on mingw32 which was breaking target
+generation on stratum.
+
+
+Version 2.8.2 - October 11, 2012
+
+- Reinstate the history on dynamic intensity mode to damp fluctuations in
+intensity but use an upper limit on how much the value can increase at any time
+to cope with rare overflows.
+- Create a fix-protocol option which prevents cgminer from switching to stratum
+if it's detected.
+- Simplify target generation code.
+- Add support for client.get_version for stratum.
+- Use a 64 bit unsigned integer on the diff target to generate the hex target.
+- Update reconnect message to show whole address including port.
+- Look for null values and parse correct separate array entries for url and port
+with client reconnect commands for stratum.
+- The command for stratum is client.reconnect, not mining.reconnect.
+- Only copy the stratum url to the rpc url if an rpc url does not exist.
+- Implement rudimentary mining.reconnect support for stratum.
+- Ignore the value of stratum_active on calling initiate_stratum and assume
+we're always trying to reinitiate it, and set the active flag to false in that
+function.
+- stratum auth can be unset if we fail to authorise on subsequent calls to
+auth_stratum which undoes the requirement of setting it in one place so set it
+in pool_active.
+
+
+Version 2.8.1 - October 8, 2012
+
+- Use the stratum url as the rpc url advertised if we switch to it.
+- Count an invalid nonce count as a hardware error on opencl.
+- Count each stratum work item as local work.
+- Cope with one stratum pool being the only active pool when it dies by sleeping
+for 5 seconds before retrying to get work from it instead of getting work
+indefinitely.
+- Detect stratum outage based on either select timing out or receiving an empty
+buffer and properly re-establish connection by disabling the stratum_active
+flag, coping with empty buffers in parse_stratum.
+
+
+Version 2.8.0 - October 7, 2012
+
+- Major upgrade - support for the stratum mining protocol.
+- Fix various modminer warnings on mingw.
+- Fix sign warning on windows build for bitforce.
+- Cast socketfail to integer since SOCKET is an unsigned int on windows.
+- Use strtod not strtol for bitforce temp backup.
+- Cope with broken drivers returning nonsense values for bitforce temperatures.
+- Minor warning fixes.
+- Use the stratum thread to detect when a stratum pool has died based on no
+message for 2 minutes.
+- Only set the stratum auth flag once and once the stratum thread is started,
+use that to set/unset the stratum active flag.
+- Only hand off to stratum from getwork if we succeed in initiating the
+protocol.
+- Target should only be 32 bytes copied.
+- Use a static array for work submission data instead of stack memory.
+- Clear the buffer data before sprinting to it.
+- Clear work stratum strings before setting them and add them to debug output.
+- Drop stratum connect failed message to verbose level only since it's a regular
+probing message.
+- TCP Keepalive in curl is only in very recent versions and not required with
+regular messages on stratum anyway.
+- Move stratum sockets to curl infrastructure with locking around send+recv to
+begin support for proxies and ssl.
+- Make detect stratum fail if a proxy has been set up.
+- Stratum does not currently have any proxy support so do not try to switch to
+stratum if a proxy has been specified.
+- Windows doesn't work with MSG_PEEK on recv so move to a continuously updating
+buffer for incoming messages.
+- Alloca is unreliable on windows so use static arrays in util.c stratum code.
+- Begin support for mingw stratum build.
+- Add space to reject reason.
+- Parse the reject reason where possible from stratum share submission.
+- Pass json error value to share result function to be able to parse reject
+reason in stratum.
+- Don't try to parse unneeded parameters in response to mining.subscribe.
+- Remove the sshare hash entry if we failed to send it.
+- Change notify message to info level to avoid spamming repeatedly when a pool
+is down.
+- Check the stratum pool difference has not changed compared to the work diff
+when testing whether a share meets the target or not and retarget if necessary.
+- Bit error in target calculation for stratum.
+- Set work_block in gen_stratum_work for when work is reused to avoid thinking
+it's all stale.
+- Offset the current block detection to the prev block hash.
+- We should be testing for id_val, not id in parse stratum response.
+- Make target on stratum scale to any size by clearing sequential bits according
+to diff.
+- Correct target calculation in gen_stratum_work.
+- If a share result has an error code but still has an id, it is likely a
+reject, not an error.
+- Initiate stratum the first time in pool_active only, allowing us to switch to
+it on getting a failed getwork and detecting the presence of stratum on the url
+at that time.
+- Use 5 second timeout on sock full for now as a temporary workaround.
+- If no stratum url is set by the end of the detect stratum routine, copy the
+sockaddr url.
+- Make all buffers slightly larger to prevent overflow.
+- Make the stratum recv buffer larger than the recvsize.
+- Userpass needs to be copied to user and pass earlier to allow stratum
+authorisation to work with it.
+- Store a sockaddr url of the stripped url used in determining sockaddr to not
+confuse it with the stratum url and fix build warnings.
+- Decrease the queued count with stratum work once it's staged as well.
+- Allow the stratum retry to initiate and auth stratum in pool_alive to make
+sure the stratum thread is started.
+- Avoid duplicating pool->rpc_url and setting pool->stratum_url twice to itself.
+- Detect if a getwork based pool has the X-Stratum header on startup, and if so,
+switch to the stratum based pool.
+- Comment update.
+- Minor message change.
+- Create a work item from a "clean" request from stratum allowing the new block
+to be detected and the appropriate block change message to be given.
+- Use statically allocated stratum strings in struct work to cope with the
+inability to safely deallocate dynamically allocated ram.
+- Use the current pool when deciding whether to reuse work from a stratum source
+rather than the work's previous pool.
+- Copy the stratum url to the rpc url to avoid none being set.
+- Provide locking around stratum send operations to avoid races.
+- Submit shares from stratum through the abstracted submit share function
+detecting what message they belong to and showing the data from the associated
+work, and then deleting it from the hash.
+- Use a more robust mechanism to obtain a \n terminated string over a socket.
+- Abstract out share submit as a function to be useable by stratum.
+- Rename parse_stratum to parse_method as it is only for stratum messages that
+contain methods.
+- Display stratum as mechanism in status line when current pool is running it.
+- Count each stratum notify as a getwork equivalent.
+- Correct nonce submitted with share.
+- Extranonce2 should be added before coinbase2.
+- We should be hashing the binary coinbase, not the hex one.
+- Fix endianness of nonce submitted for stratum.
+- Check that stratum is already active in initiate_stratum to avoid
+de-authorising ourselves by subscribing again.
+- Begin implementing a hash database of submissions and attempt sending results.
+- Copy parameters from stratum work required for share submission.
+- Set lagging flag on first adding a pool to prevent pool slow warning at
+startup.
+- Fix work->target being a 32 byte binary in gen_stratum_work.
+- Store and display stripped url in its own variable.
+- Create machinery to divert work requests to stratum.
+- Generate the work target in gen_stratum_work, setting default diff to 1 in
+case it is not yet set.
+- Generate work data, midstate and hash1 in gen_stratum_work.
+- Generate header created from stratum structures in gen_stratum_work.
+- Generate merkle root hash in gen_stratum_work.
+- Generate the coinbase for generation of stratum based work.
+- The number of transactions is variable so make merkle a variable length
+dynamically allocated array and track how many there are for stratum.
+- Rename nonce2 to n2size reflecting that it's a size variable and not the
+actual nonce.
+- Provide rudimentary support for stratum clean work command in the stratum
+thread.
+- Cope with pools being removed in the stratum thread.
+- Use the pool sock value directly in the stratum thread in case it changes
+after reconnecting.
+- Create a stratum thread per pool that has stratum that monitors the socket and
+serves received data.
+- Check return value of stratum_parse.
+- Complete authorisation in stratum.
+- Implement stratum parsing of notify parameters and storing them in the pool
+stratum work structure.
+- Create helper functions for duplicating json strings to avoid keeping json
+references in use.
+- Append \n in the sock_send function instead of adding it when constructing
+json in stratum.
+- Don't keep any json references around with stratum structures.
+- Create parse_stratum function that hands off stratum parameters to other
+functions to manage pool stratum work struct variables. Implement mining
+difficulty setting.
+- Create helper functions for checking when a socket is ready to read on and
+receive a single line at a time. Begin stratum authorisation process.
+- Provide a helper function for reading a single \n terminated string from a
+socket.
+- Create a stratum work structure to store current work variables.
+- Test specifically for stratum being active in pool_active.
+- Detect stratum in common place when adding urls, and use a bool to tell us
+when it's active.
+- Fix warnings.
+- Extract and store various parameters on stratum init confirming successful
+mining notify.
+- Use existing socket macros and close the socket on failure in init stratum.
+- Initiate stratum and grab first json result.
+- Get detailed addressinfo from the parsed URL for future raw socket usage when
+possible. IPV4 only for now.
+- Prepare for getaddrinfo call.
+- Add data structures to pool struct for socket communications.
+- Put all socket definitions in util.h to allow reusing by added socket
+functions to be used in util.c.
+
+
+Version 2.7.7 - October 7, 2012
+
+- Fix unused warnings on ming build.
+- Fix sign warning in ocl.c
+- fds need to be zeroed before set in modminer.
+- Put scrypt warning on separate line to avoid 0 being shown on windows as
+bufsize.
+- Display correct pool number when block is found.
+- Prevent corrupt values returned from the opencl code from trying to read
+beyond the end of the buffer by masking the value to a max of 15.
+- Icarus USB write failure is also a comms error
+- api.c DEBUG message has no paramter
+- Icarus catch more USB errors and close/reopen the port
+- API-README update cgminer verison number
+- hashmeter fix stats kh/s on 32bit windows
+
+
+Version 2.7.6 - September 24, 2012
+
+- Reorder libztex header include order to fix missing struct definition.
+- Display share difficulty on log with a shortened hash display on submission.
+- API stats add some pool getwork difficulty stats
+- Ignore any pings pushed to the worker threads if the thread is still paused to
+prevent it being enabled and disabled repeatedly.
+- README - FAQ - usermod group - shouldn't remove other groups
+- Test for sequential getwork failures on a pool that might actually be up but
+failing to deliver work as we may end up hammering it repeatedly by mistake.
+- reduce windows compile warnings
+- util.c - bug - proxy - no data end condition
+- As we average gpu time over 5 work intervals for dynamic GPU intensity, there
+is no need to maintain a rolling average and it avoids the potential long term
+corruption of a single overflow value.
+- Test for the now-automatically exported variable AMDAPPSDKROOT when looking
+for the presence of the OpenCL headers.
+- API don't change 'Diff1 Shares' - backward compatability FTW
+- miner.php highlighting correctly handling difficulty
+- API - Add last share difficulty for devices and pool
+- Store and report Accepted,Rejected,Stale difficulty in the summary and API
+- WorkTime - display prevblock for scrypt
+- api.c remove compile warnings
+- Calculate work difficulty for each getwork and display with WorkTime debug
+- remove MMQ unused variable warning
+- FPGA - allow long or short device names in detect code + style police
+- WorkTime - multiple nonce per work and identify the work source
+- Optional WorkTime details with each Accepted/Rejected work item
+- Icarus - ignore hardware errors in timing mode
+- miner.php oops - mistype
+- miner.php by default don't display IP/Port numbers in error messages
+- api.c all STATUS messages automatically escaped
+- api.c add missing escape for comma in MSG_PGAUNW
+- API add display of and setting queue,scantime,expiry
+- HW: dont submit bad shares
+- save individual pool proxy settings to config
+- --default-config - allow command line to define the default configuration file
+for loading and saving
+- API-README update for pools proxy info
+- README URL proxy must use quote so show in the example
+- bug: remove proxy: from the front of the proxy used
+- CURL support for individual proxy per pool and all proxy types
+- README spelling/etc
+- README - FPGA device FAQ
+- HW: error counter auto for all devices - ztex code not fixed
+- API pgaidentify - unsupported message should be a warning
+- API/BFL identify a device - currently only BFL to flash the led
+- BFL add throttle count to internal stats + API
+- BFL: missing device id in log message
+- miner.php correct to new Diff1 Work field names
+- API add device diff1 work
+- API-README update
+- api.c Correct diff1 field name
+- count device diff1 shares
+- API-README more debug parameter information
+- API allow full debug settings control
+
+
+Version 2.7.5 - August 31, 2012
+
+- Adjust opencl intensity when adjusting thread count to prevent it getting
+pegged at a value below the minimum threads possible.
+- miner.h max_hashes -> int64_t
+- Keep the local block number in the blocks structs stored and sort them by
+number to guarantee we delete the oldest when ageing the block struct entries.
+- Use correct sdk version detection for SDK 2.7
+- Revert "Pick worksize 256 with Cypress if none is specified."
+- Test for lagging once more in queue_request to enable work to leak to backup
+pools.
+- There is no need to try to switch pools in select_pool since the current pool
+is actually not affected by the choice of pool to get work from.
+- Only clear the pool lagging flag if we're staging work faster than we're using
+it.
+- needed flag is currently always false in queue_request. Remove it for now.
+- thr is always NULL going into queue_request now.
+
+
+Version 2.7.4 - August 23, 2012
+
+- Perform select_pool even when not lagging to allow it to switch back if needed
+to the primary.
+- Simplify macros in output kernels avoiding apparent loops and local variables.
+- Carry the needed bool over the work command queue.
+- Move the decision to queue further work upstream before threads are spawned
+based on fine grained per-pool stats and increment the queued count immediately.
+- Track queued and staged per pool once again for future use.
+- OpenCL 1.0 does not have native atomic_add and extremely slow support with
+atom_add so detect opencl1.0 and use a non-atomic workaround.
+- Pools: add RollTime info to API 'stats' and 'Stats' button in miner.php
+
+
+Version 2.7.3 - August 22, 2012
+
+- Minimise the number of getwork threads we generate.
+
+
+Version 2.7.2 - August 22, 2012
+
+- Pick worksize 256 with Cypress if none is specified.
+- Give warning with sdk2.7 and phatk as well.
+- Whitelist sdk2.7 for diablo kernel as well.
+- Only keep the last 6 blocks in the uthash database to keep memory usage
+constant. Storing more is unhelpful anyway.
+- BFL Flash - always distribute source
+- Increase kernel versions signifying changed APIs.
+- BFL flash - include source in builds and more FPGA-README
+- Check we haven't staged work while waiting for a curl entry before proceeding.
+- Use atomic ops to never miss a nonce on opencl kernels, including nonce==0,
+also allowing us to make the output buffer smaller.
+- Remove compile errors/warnings and document compile/usage in FPGA-README
+- bitforce-firmware-flash.c by Luke-jr
+- Ignore the submit_fail flag when deciding whether to recruit more curls or not
+since we have upper bounds on how many curls can be recruited, this test is
+redundant and can lead to problems.
+- API-README update cgminer version number
+- API-README fix groups P: example mistake
+- API-README add COIN and other edits
+- gpu->hit should be reset on new work as well.
+- Do not add time to dynamic opencl calculations over a getwork.
+- miner.php allow 'coin' is custom pages
+
+
+Version 2.7.1 - August 21, 2012
+
+- Update windows build instructions courtesy of sharky.
+- Increase max curls to number of mining threads + queue * 2, accounting for up
+and downstream comms.
+- Queue enough requests to get started.
+- There is no point trying to clone_work in get_work() any more since we clone
+on every get_work_thread where possible.
+- There is no point subtracting 1 from maxq in get_work_thread.
+- Only set lagging flag once there are no staged work items.
+- select_pool does not switch back to the primary once lagging is disabled.
+- miner.php allow page title to be defined in myminer.php
+- Free work before retrying in get_work_thread.
+- Increment total work counter under mutex lock.
+- Increment the queued count after the curl is popped in case there's a delay
+waiting on curls and we think we've queued work when in fact we're waiting
+- API new command 'coin' with mining information
+- Do the dynamic timing in opencl code over a single pass through scanhash to
+make sure we're only getting opencl times contributing to the measured inte
+- Increase curl reaping time to 5 minutes since comms between  curl requests can
+be 2 mins apart with lots of rolltime.
+- No need for extra variable in hash_push.
+- Remove short options -r and -R to allow them to be reused and remove readme
+entries for deprecated options.
+- Avoid attempting to recursively lock the console mutex by disabling warnings
+in gpu_fanpercent when fanspeed monitoring fails on windows. Debugged by l
+- Deprecate the opt_fail_pause parameter, leaving a null placeholder for
+existing configurations.
+- Don't pause after failed getwork, set lagging flag and reassess.
+- Add message to share if it's a resubmit.
+- We should not be pausing in trying to resubmit shares.
+- Get rid of the extending fail pause on failed connects since we discard work
+after a period.
+- get_work always returns true so turn it into a void function.
+- get_work never returns false so get rid of fail pause loop.
+- Get rid of pause and retry from get_upstream_work so we only do it from one
+place.
+- Deprecate the opt_retries feature as no one wants cgminer to automatically
+abort. Leave a null placeholder for configurations that still have it.
+- Reinstate fix ADL gpu-map not working when there are more ADL devices than
+openCL patch by Nite69. Add virtual adl mapping for when none is specified o
+- miner.php show summary Diff1 Shares total
+- miner.php fix Work Utility totals
+- miner.php format new Work Utility and Diff1 Shares
+- API V1.17 show Work Utility and Diff1 Shares
+
+
+
+Version 2.7.0 - August 18, 2012
+
+- Introduce a new statistic, Work Utility, which is the number of difficulty 1
+shares solved per minute. This is useful for measuring a relative rate of work
+that is independent of reject rate and target difficulty.
+- Implement a new pool strategy, BALANCE, which monitors work performed per pool
+as a rolling average every 10 minutes to try and distribute work evenly over all
+the pools. Do this by monitoring diff1 solutions to allow different difficulty
+target pools to be treated equally, along with solo mining. Update the
+documentation to describe this strategy and more accurately describe the
+load-balance one.
+- Getwork fail was not being detected. Remove a vast amount of unused variables
+and functions used in the old queue request mechanism and redefine the getfail
+testing.
+- Don't try to start devices that don't support scrypt when scrypt mining.
+- 0 is a valid return value for read so only break out if read returns -1.
+- Consider us lagging only once our queue is almost full and no staged work.
+- Simplify the enough work algorithm dramatically.
+- Only queue from backup pools once we have nothing staged.
+- Don't keep queueing work indefinitely if we're in opt failover mode.
+- Make sure we don't opt out of queueing more work if all the queued work is
+from one pool.
+- Set lagging flag if we're on the last of our staged items.
+- Reinstate clone on grabbing work.
+- Grab clones from hashlist wherever possible first.
+- Cull all the early queue requests since we request every time work is popped
+now.
+- Keep track of staged rollable work item counts to speed up clone_available.
+- Make expiry on should_roll to 2/3 time instead of share duration since some
+hardware will have very fast share times.
+- Do the cheaper comparison first.
+- Check that we'll get 1 shares' worth of work time by rolling before saying we
+should roll the work.
+- Simplify all those total_secs usages by initialising it to 1 second.
+- Overlap queued decrementing with staged incrementing.
+- Artificially set the pool lagging flag on pool switch in failover only mode as
+well.
+- Artificially set the pool lagging flag on work restart to avoid messages about
+slow pools after every longpoll.
+- Factor in opt_queue value into enough work queued or staged.
+- Roll work whenever we can on getwork.
+- Queue requests for getwork regardless and test whether we should send for a
+getwork from the getwork thread itself.
+- Get rid of age_work().
+- 0 is a valid return value for read so only break out if read returns -1.
+- Offset libusb reads/writes by length written as well in ztex.
+- Cope with timeouts and partial reads in ztex code.
+- fpga serial I/O extra debug (disabled by default)
+
+
+Version 2.6.5 - August 15, 2012
+
+- Don't try to get bitforce temperature if we're polling for a result to
+minimise the chance of interleaved responses.
+- Set memory clock based on memdiff if present from with engine changes,
+allowing it to parallel manual changes from the menu as well.
+- Increase the timeout on bitforce as per Paul Sheppard's suggestion to account
+for throttling + work time + excess.
+- Fix ADL gpu-map not working when there are more ADL devices than openCL.
+Initial patch supplied by Nite69. Modified to suit.
+- Windows' timer resolution is limited to 15ms accuracy. This was breaking
+dynamic intensity since it tries to measure below this. Since we are repeatedly
+sampling similar timeframes, we can average the gpu_us result over 5 different
+values to get very fine precision.
+- Fix harmless unused warnings in scrypt.h.
+- api.c typo
+- API allow display/change failover-only setting
+- Check we are not lagging as well as there is enough work in getwork.
+- Minimise locking and unlocking when getting counts by reusing shared mutex
+lock functions.
+- Avoid getting more work if by the time the getwork thread is spawned we find
+ourselves with enough work.
+- The bitforce buffer is cleared and hw error count incremented on return from a
+failed send_work already so no need to do it within the send_work function.
+- miner.php allow a custom page section to select all fields with '*' - e.g. to
+create a STATS section on a custom page
+- Escape " and \ when writing json config file
+- miner.php optional single rig totals (on by default)
+
+
+Version 2.6.4 - August 7, 2012
+
+- Convert the serial autodetect functions to use int instead of char to
+enumerate devices.
+- Make the serial open timeout for BFL generically 1 second on windows.
+- Deuglify windows autodetect code for BFL.
+- There is no point zeroing temperature in BFL if we fail to get a response, and
+we should register it as a HW error, suggesting throttling.
+- Update SCRYPT README with information about HW errors.
+- Use the scrypt CPU code to confirm results from OCL code, and mark failures as
+HW errors, making it easier to tune scrypt parameters.
+- We may as well leave one curl still available per pool instead of reaping the
+last one.
+- Need to recheck the pool->curls count on regaining the pool lock after the
+pthread conditional wait returns.
+- Display reaped debug message outside mutex lock to avoid recursive locking.
+- Add specific information when ADL detects error -10 saying the device is not
+enabled.
+- api.c update API start message and include port number
+- miner.php ignore arg when readonly
+- miner.php allow pool inputs: delete, addpool, poolpriority
+
+
+Version 2.6.3 - August 5, 2012
+
+- Count likely throttling episodes on bitforce devices as hardware errors.
+- Style cleanups.
+- Use FTD2XX.DLL on Windows to autodetect BitFORCE SHA256 devices.
+- Make pool_disabled the first in the enums == 0, fixing the pool enabled count
+which compares if value is not enabled before enabling it.
+- Correct writing of scrypt parameters to config file based on command line
+parameters only.
+- Use different variables for command line specified lookup gap and thread
+concurrency to differentiate user defined versus auto chosen values.
+- Queue a request on pool switch in case we have no work from the new pool yet.
+- Display failover only mode in pool menu and allow it to be toggled live.
+- Reinstate check for system queueing lag when the current pool's queue is maxed
+out, there is no staged work, and the work is needed now.
+- There is no need for pool active testing to be mandatory any more with queue
+request changes.
+- Fix harmless warnings.
+- Check the current staged and global queued as well before queueing requests.
+Discard stales before ageing work in the watchdog thread. Queue requests after
+discarding and ageing work in watchdog thread. Display accurate global queued in
+curses output. Reuse variable in age_work().
+- The queueing mechanism has become a complex state machine that is no longer
+predictable. Rewrite it from scratch watching only current queues in flight and
+staged work available on a pool by pool basis.
+- API remove unused warning in non-GPU compile
+- api.c in linux allow to open a closed socket in TIME_WAIT
+- Queue an extra request whenever staged work drops below mining thread count in
+hash_pop.
+- Update debian package configs to v2.6.2
+
+
+Version 2.6.2 - August 3, 2012
+
+- Scrypt mining does not support block testing yet so don't try to print it.
+- Clear the bitforce buffer whenever we get an unexpected result as it has
+likely throttled and we are getting cached responses out of order, and use the
+temperature monitoring as a kind of watchdog to flush unexpected results.
+- It is not critical getting the temperature response in bitforce so don't
+mandatorily wait on the mutex lock.
+- Check there is a cutoff temp actually set in bitforce before using it as a cut
+off value otherwise it may think it's set to zero degrees.
+- We dropped the temporary stopping of curl recruiting on submit_fail by
+mistake, reinstate it.
+- Make threads report in either side of the scanhash function in case we miss
+reporting in when restarting work.
+- Don't make mandatory work and its clones last forever.
+- Make test work for pool_active mandatory work items to smooth out staged work
+counts when in failover-only mode.
+- Add debugging output when work is found stale as to why.
+- Print the 3 parameters that are passed to applog for a debug line in
+bitforce.c
+- Clear bitforce buffer on init as previously.
+- Add some headroom to the number of curls available per pool to allow for
+longpoll and sendwork curls.
+- Revert "Revert "Change BFL driver thread initialising to a constant 100ms
+delay between devices instead of a random arrangement.""
+- Revert "Remove bitforce_thread_init"
+- Show the correct base units on GPU summary.
+- Differentiate between the send return value being a bool and the get return
+value when managing them in bitforce scanhash.
+- 23a8c60 Revert "bitforce: Skip out of sending work if work restart requested"
+
+
+Version 2.6.1 - July 30, 2012
+
+- Display scrypt as being built in as well.
+- Fix build warning about KL_SCRYPT when built without scrypt support.
+- Remove the low hash count determinant of hardware being sick. A low hash rate
+can be for poor network connectivity or scrypt mining, neither of which are due
+to a sick device.
+- api.c poolpriority changes
+
+
+Version 2.6.0 - July 29, 2012
+
+- Display kilohash when suitable, but store the global mhash value still truly
+in megahashes to not break the API output.
+- Don't try and print curses output for devices that won't fit on the screen.
+- Add scrypt documentation in the form of a separate readme.
+- Fix build error without scrypt enabled.
+- Limit total number of curls recruited per pool to the number of mining threads
+to prevent blasting the network when we only have one pool to talk to.
+- bitforce: Skip out of sending work if work restart requested
+- Keep a counter of enabled pools and use that instead of iterating over the
+pool list. Use that value to ensure we don't set the last remaining active pool
+to the rejecting state.
+- fpgautils: add support for 57.6 kBd serial
+- miner.php add a socket RCV timeout for if cgminer is hung and the API thread
+is still running
+- Limit thread concurrency for scrypt to 5xshaders if shaders is specified.
+- Simplify repeated use of gpus[gpu]. in ocl.c
+- Find the nearest power of 2 maximum alloc size for the scrypt buffer that can
+successfully be allocated and is large enough to accomodate the thread
+concurrency chosen, thus mapping it to an intensity.
+- Don't make opt_scrypt mandatory blocking with opencl code.
+- Update kernel versions reflecting changes in the API.
+- Make the thread concurrency and lookup gap options hidden on the command line
+and autotune parameters with a newly parsed --shaders option.
+- Fix target testing with scrypt kernel as it would have been missing shares
+below target.
+- Bugfix: Use a mutex to control non-curses output
+- Simplify code to a single vprintf path for curses-less printing
+- Move opt_quiet check to my_log_curses, so it works for curses-less builds
+- Use log_generic for vapplog to cut down on code duplication
+- Add space to log output now that there is more screen real estate available.
+- BFL force all code to timeout to avoid hanging
+- Bugfix: Copy argv[0] given to dirname()
+- Always create the largest possible padbuffer for scrypt kernels even if not
+needed for thread_concurrency, giving us some headroom for intensity levels.
+- Use the detected maximum allocable memory on a GPU to determine the optimal
+scrypt settings when lookup_gap and thread_concurrency parameters are not given.
+- Check the maximum allocable memory size per opencl device.
+- Add debugging output if buffer allocation fails for scrypt and round up
+bufsize to a multiple of 256.
+- Nonce testing for btc got screwed up, leading to no accepted shares. Fix it.
+- Display size of scrypt buffer used in debug.
+- Allow intensities up to 20 if scrypt is compiled in.
+- Add name to scrypt kernel copyright.
+- Allow lookup gap and thread concurrency to be passed per device and store
+details in kernel binary filename.
+- Ignore negative intensities for scrypt.
+- Change the scale of intensity for scrypt kernel and fix a build warning.
+- Correct target value passed to scrypt kernel.
+- Use 256 output slots for kernels to allow 1 for each worksize.
+- Test the target in the actual scrypt kernel itself saving further
+calculations.
+- Reinstate GPU only opencl device detection.
+- Decrease lookup gap to 1. Does not seem to help in any way being 2.
+- Fix build.
+- Make pad0 and pad1 local variable in scrypt kernel.
+- Constify input variable in scrypt kernel.
+- Send correct values to scrypt kernel to get it finally working.
+- Create command queue before compiling program in opencl.
+- Detach pthread from within the api thread in case it is terminated due to not
+being instantiated before pthread_cancel is called from main, leading to a
+segfault.
+- Debug output per thread hashrate is out by a factor of 1000.
+- Initialise mdplatform.
+- Find the gpu platform with the most devices and use that if no platform option
+is passed.
+- Allow more platforms to be probed if first does not return GPUs.
+- Fix external scrypt algo missing.
+- Limit scrypt to 1 vector.
+- Handle KL_SCRYPT in config write.
+- Get rid of stuff.
+- Don't enqueuewrite buffer at all for pad8 and pass work details around for
+scrypt in dev_blk.
+- Set the correct data for cldata and prepare for pad8 fixes.
+- Bugfix: Fix build without curses but with OpenCL
+- Find the gpu platform with the most devices and use that if no platform option
+is passed.
+- Allow more platforms to be probed if first does not return GPUs.
+- Get rid of spaces in arrays in scrypt kernel.
+- Start with smaller amount of hashes in cpu mining to enable scrypt to return
+today sometime.
+- Show Khash hashrates when scrypt is in use.
+- Free the scratchbuf memory allocated in scrypt and don't check if CPUs are
+sick since they can't be. Prepare for khash hash rates in display.
+- Add cpumining capability for scrypt.
+- Set scrypt settings and buffer size in ocl.c code to be future modifiable.
+- Cope with when we cannot set intensity low enough to meet dynamic interval by
+inducing a forced sleep.
+- Make dynamic and scrypt opencl calls blocking.
+- Calculate midstate in separate function and remove likely/unlikely macros
+since they're dependent on pools, not code design.
+- bitforce: Use "full work" vs "nonce range" for kernel name
+- Display in debug mode when we're making the midstate locally.
+- Fix nonce submission code for scrypt.
+- Make sure goffset is set for scrypt and drop padbuffer8 to something
+manageable for now.
+- Set up buffer8 for scrypt.
+- Build fix for opt scrypt.
+- Don't check postcalc nonce with sha256 in scrypt.
+- Don't test nonce with sha and various fixes for scrypt.
+- Make scrypt buffers and midstate compatible with cgminer.
+- Use cgminer specific output array entries in scrypt kernel.
+- Provide initial support for the scrypt kernel to compile with and mine scrypt
+with the --scrypt option.
+- Enable completely compiling scrypt out.
+- Begin import of scrypt opencl kernel from reaper.
+- bitforce_get_result returns -1 on error now.
+- Check return value of read in BFgets
+- Bugfix: Make our Windows nanosleep/sleep replacements standards-compliant
+(which fixes nmsleep) and include compat.h for bitforce (for sleep)
+- rpc: Use a single switch statement for both stringifications of cgpu->status
+- Fix whitespace mangling.
+- miner.php fix rig # when miners fail
+- Only try to shut down work cleanly if we've successfully connected and started
+mining.
+- Use switch statement for cgpu->status and fix spelling.
+- Abbrv. correction
+- Bugfix: Don't declare devices SICK if they're just busy initialising
+- Bugfix: Calculate nsec in nmsleep correctly
+- Bugfix: Adapt OpenCL scanhash errors to driver API change (errors are now -1,
+not 0)
+- Remove superfluous ave_wait
+- Put kname change for broken nonce-range back in
+- Add average wait time to api stats
+- Change BFL driver thread initialising to a constant 100ms delay between
+devices instead of a random arrangement.
+- Spelling typo.
+- Time opencl work from start of queueing a kernel till it's flushed when
+calculating dynamic intensity.
+- Modify te scanhash API to use an int64_t and return -1 on error, allowing zero
+to be a valid return value.
+- Check for work restart after the hashmeter is invoked for we lose the hashes
+otherwise contributed in the count.
+- Remove disabled: label from mining thread function, using a separate
+mt_disable function.
+- Style changes.
+- Missed one nonce-range disabling.
+- Add average return time to api stats
+- miner.php allow rig names in number buttons
+- Remove bitforce_thread_init The delay thing does nothing useful... when long
+poll comes around, all threads restart at the same time anyway.
+- Change timeouts to time-vals for accuracy.
+- fix API support for big endian machines
+- Cope with signals interrupting the nanosleep of nmsleep.
+- Use standard cfsetispeed/cfsetospeed to set baud rate on *nix
+- miner.php split() flagged deprecated in PHP 5.3.0
+- More BFL tweaks. Add delay between closing and reopening port. Remove buffer
+clear in re-init Add kernel type (mini-rig or single)
+- Make long timeout 10seconds on bitforce for when usleep or nanosleep just
+can't be accurate...
+
+
+Version 2.5.0 - July 6, 2012
+
+- Fix --benchmark not working since the dynamic addition of pools and pool
+stats.
+- Make disabling BFL nonce range support a warning since it has to be explicitly
+enabled on the command line now.
+- miner.php allow renaming table headers
+- Make bitforce nonce range support a command line option --bfl-range since
+enabling it decrease hashrate by 1%.
+- Add sanity checking to make sure we don't make sleep_ms less than 0 in
+bitforce.
+- The fastest minirig devices need a significantly smaller starting sleep time.
+- Use a much shorter initial sleep time to account for faster devices and nonce
+range working, and increase it if nonce range fails to work.
+- Use nmsleep instead of usleep in bitforce.
+- Provide a ms based sleep function that uses nanosleep to avoid the inaccuracy
+of usleep on SMP systems.
+- delay_time_ms is always set so need not be initialised in bitforce.
+- Increase bitforce timeout to 10 seconds.
+- Add more hysteresis and poll ~5 times to allow for timer delays in bitforce
+devices.
+- miner.php allow alternating line colours (off by default)
+- Display the actual duration of wait when it is greater than the cutoff.
+- Set nonce to maximum once we determine nonce range support is broken.
+- Initial wait time is always known so no need to zero it beforehand in
+bitforce.
+- No point counting wait time until the work is actually sent to bitforce
+devices.
+- Use string comparison functions instead of explicit comparisons.
+- Account for wait_ms time when nonce_range is in use on BFL.
+- Split nonces up into 1/5 chunks when nonce range is supported.
+- limit clear buffer iterations.
+- Ad fd check to clear buffer.
+- miner.php remove incorrect 'DATE' error message
+- miner.php allow summary header in custom pages
+- Disable nonce range support in BFL when broken support is detected.
+- Restart_wait is only called with a ms value so incorporate that into the
+function.
+- Only try to adjust dev width when curses is built in.
+- miner.php define custom sum fields as a simple array
+- Fix off-by-one error in nonce increment in bfl.
+- Use BE when setting nonce in bitforce nonce range work.
+- Enable nonce range in the normal init sequence for bfl.
+- Queue extra work at 2/3 differently depending on whether we're using nonce
+range or not.
+- Initially enable support for nonce range support on bfl, splitting nonces up
+into 3/4 size and only disable it if it fails on work submit.
+- Attempt to detect nonce range support in BFL by sending work requring its
+support.
+- Limit retrying on busy for up to BITFORCE_TIMEOUT_MS
+- Attempt to initialise while bitforce device returns BUSY.
+- Extend length of string that can be passed to BFL devices.
+- Fix signedness warning.
+- Adjust device width column to be consistent.
+- Use cgpu-> not gpus[] in watchdog thread.
+- Add api stats (sleep time)
+- Timing tweaks Added long and short timeouts, short for detecting throttling,
+long to give up totally. Reset sleep time when device re-initialised Still check
+results after timeout Back up a larger time if result on first poll.
+- Add API Notify counter 'Comms Error'
+- Style police on api.c
+- Do all logging outside of the bitforce mutex locking to avoid deadlocks.
+- Remove applog call from bfwrite to prevent grabbing nested mutexes.
+- Bitforce style changes.
+- Minor style changes.
+- Remove needless roundl define.
+- Made JSON error message verbose.
+- Fine-tune timing adjustment. Also remove old work_restart timing.
+- Check for gpu return times of >= 0, not just 0, to fix intensity dropping to
+-10.
+- Restart is zeroed in the mining thread so no need to do it inside the bitforce
+code.
+- More improvements to comms. BFL return nothing when throttling, so should not
+be considered an error. Instead repeat with a longer delay.
+- Polling every 10ms there's not much point checking the pthread_cond_timedwait
+as it just adds overhead. Simply check the value of work_restart in the bfl main
+polling loop.
+- Use a pthread conditional that is broadcast whenever work restarts are
+required. Create a generic wait function waiting a specified time on that
+conditional that returns if the condition is met or a specified time passed to
+it has elapsed. Use this to do smarter polling in bitforce to abort work, queue
+more work, and check for results to minimise time spent working needlessly.
+- Add busy time to wait time.
+- api.c put version up to 1.14
+- Add tiny delay after writing to BFL Change BFL errors to something more human
+readable Send work busy re-tries after 10ms delay
+
+
+Version 2.4.4 - July 1, 2012
+
+- Fix builds on non gnu platforms.
+- api.c ensure old mode is always available when not using --api-groups + quit()
+on param errors
+- Implement rudimentary X-Mining-Hashrate support.
+- Detect large swings in temperature when below the target temperature range and
+change fan by amounts dependant on the value of tdiff.
+- Adjust the fanspeed by the magnitude of the temperature difference when in the
+optimal range.
+- Revert "Restarting cgminer from within after ADL has been corrupted only leads
+to a crash. Display a warning only and disable fanspeed monitoring."
+- api.c fix json already closed
+- implement and document API option --api-groups
+- Put upper bounds to under 2 hours that work can be rolled into the future for
+bitcoind will deem it invalid beyond that.
+- define API option --api-groups
+- api.c allow unwell devices to be enabled so they can be cured
+- miner.php - fix/enable autorefresh for custom pages
+- miner.php allow custom summary pages - new 'Mobile' summary
+- Work around pools that advertise very low expire= time inappropriately as this
+leads to many false positives for stale shares detected.
+- Only show ztex board count if any exist.
+- There is no need for work to be a union in struct workio_cmd
+- fpgautils.c include a debug message for all unknown open errors
+- Don't keep rolling work right up to the expire= cut off. Use 2/3 of the time
+between the scantime and the expiry as cutoff for reusing work.
+- Log a specific error when serial opens fail due to lack of user permissions
+- Increase GPU timing resolution to microsecond and add sanity check to ensure
+times are positive.
+- Opencl code may start executing before the clfinish order is given to it so
+get the start timing used for dynamic intensity from before the kernel is
+queued.
+- fpgautils.c - set BAUD rate according to termio spec
+- fpgautils.c - linux ordering back to the correct way
+- miner.php remove unneeded '.'s
+- miner.php add auto refresh options
+- miner.php add 'restart' next to 'quit'
+- miner.php make fontname/size configurable with myminer.php
+- Make the pools array a dynamically allocated array to allow unlimited pools to
+be added.
+- Make the devices array a dynamically allocated array of pointers to allow
+unlimited devices.
+- Dynamic intensity for GPUs should be calculated on a per device basis. Clean
+up the code to only calculate it if required as well.
+- Use a queueing bool set under control_lock to prevent multiple calls to
+queue_request racing.
+- Use the work clone flag to determine if we should subtract it from the total
+queued variable and provide a subtract queued function to prevent looping over
+locked code.
+- Don't decrement staged extras count from longpoll work.
+- Count longpoll's contribution to the queue.
+- Increase queued count before pushing message.
+- Test we have enough work queued for pools with and without rolltime
+capability.
+- As work is sorted by age, we can discard the oldest work at regular intervals
+to keep only 1 of the newest work items per mining thread.
+- Roll work again after duplicating it to prevent duplicates on return to the
+clone function.
+- Abstract out work cloning and clone $mining_threads copies whenever a rollable
+work item is found and return a clone instead.
+- api.c display Pool Av in json
+- Take into account average getwork delay as a marker of pool communications
+when considering work stale.
+- Work out a rolling average getwork delay stored in pool_stats.
+- Getwork delay in stats should include retries for each getwork call.
+- Walk through the thread list instead of searching for them when disabling
+threads for dynamic mode.
+- Extend nrolltime to support the expiry= parameter. Do this by turning the
+rolltime bool into an integer set to the expiry time. If the pool supports
+rolltime but not expiry= then set the expiry time to the standard scantime.
+- When disabling fanspeed monitoring on adl failure, remove any twin GPU
+association. This could have been leading to hangs on machines with dual GPU
+cards when ADL failed.
+- modminer: Don't delay 2nd+ FPGAs during work restart
+- Disable OpenCL code when not available.
+- Fix openwrt crashing on regeneratehash() by making check_solve a noop.
+- FPGA - allow device detect override without an open failure
+- Fix sign warning.
+
+
+Version 2.4.3 - June 14, 2012
+
+- can_roll and should_roll should have no bearing on the cycle period within the
+miner_thread so remove it.
+- Check for strategy being changed to load balance when enabling LPs.
+- Check that all threads on the device that called get_work are waiting on
+getwork before considering the pool lagging.
+- Iterate over each thread belonging to each device in the hashmeter instead of
+searching for them now that they're a list.
+- When using rotate pool strategy, ensure we only select from alive enabled
+pools.
+- Start longpoll from every pool when load balance strategy is in use.
+- Add mandatory and block fields to the work struct. Flag any shares that are
+detected as blocks as mandatory to submit, along with longpoll work from a
+previously rejecting pool.
+- Consider the fan optimal if fanspeed is dropping but within the optimal speed
+window.
+- Fix typo in some API messages (succeess/success)
+- api.c MMQ stat bugs
+- Bugfix: Fix warnings when built without libudev support
+- Bugfix: slay a variety of warnings
+- Bugfix: modminer: Fix unsigned/signed comparison and similar warnings
+- API add ModMinerQuad support
+- Bugfix: Honour forceauto parameter in serial_detect functions
+- modminer: Temperature sensor improvements
+- modminer: Make log messages more consistent in format
+- Only adjust GPU speed up if the fanspeed is within the normal fanrange and
+hasn't been turned to maximum speed under overheat conditions.
+- ModMiner use valid .name
+- New driver: BTCFPGA ModMiner
+- Abstract generally useful FPGA code into fpgautils.c
+- API add stats for pool getworks
+- miner.php option to hide specific fields from the display
+- miner.php add version numbers to the summary page
+- Update debian configs to v2.4.2
+- Add API and FPGA READMEs into Makefile to be included in source distribution.
+- Icarus - fix unit64_t printf warnings
+
+
+Version 2.4.2 - June 2, 2012
+
+- API.class compiled with Java SE 6.0_03 - works with Win7x64
+- miner.php highlight devs too slow finding shares (possibly failing)
+- API update version to V1.11 and document changes
+- API save default config file if none specified
+- api.c save success incorrectly returns error
+- api.c replace BUFSIZ (linux/windows have different values)
+- Move RPC API content out of README to API-README
+- Open a longpoll connection if a pool is in the REJECTING state as it's the
+only way to re-enable it automatically.
+- Use only one longpoll as much as possible by using a pthread conditional
+broadcast that each longpoll thread waits on and checks if it's the current pool
+before
+- If shares are known stale, don't use them to decide to disable a pool for
+sequential rejects.
+- Restarting cgminer from within after ADL has been corrupted only leads to a
+crash. Display a warning only and disable fanspeed monitoring.
+- Icarus: fix abort calculation/allow user specified abort
+- Icarus: make --icarus-timing hidden and document it in FPGA-README
+- Icarus: high accuracy timing and other bitstream speed support
+- add-MIPSEB-to-icarus-for-BIG_ENDIAN
+- work_decode only needs swab32 on midstate under BIG ENDIAN
+- add compile command to api-example.c
+- save config bugfix: writing an extra ',' when no gpus
+- Add dpkg-source commits
+
+
+Version 2.4.1 - May 6, 2012
+
+- In the unlikely event of finding a block, display the block solved count with
+the pool it came from for auditing.
+- Display the device summary on exit even if a device has been disabled.
+- Use correct pool enabled enums in api.c.
+- Import Debian packaging configs
+- Ensure we test for a pool recovering from idle so long as it's not set to
+disabled.
+- Fix pool number display.
+- Give cgminer -T message only if curses is in use.
+- Reinit_adl is no longer used.
+- API 'stats' allow devices to add their own stats also for testing/debug
+- API add getwork stats to cgminer - accesable from API 'stats'
+- Don't initialise variables to zero when in global scope since they're already
+initialised.
+- Get rid of unitialised variable warning when it's false.
+- Move a pool to POOL_REJECTING to be disabled only after 3 minutes of
+continuous rejected shares.
+- Some tweaks to reporting and logging.
+- Change FPGA detection order since BFL hangs on an ICA
+- API support new pool status
+- Add a temporarily disabled state for enabled pools called POOL_REJECTING and
+use the work from each longpoll to help determine when a rejecting pool has
+started working again. Switch pools based on the multipool strategy once a pool
+is re-enabled.
+- Removing extra debug
+- Fix the benchmark feature by bypassing the new networking code.
+- Reset sequential reject counter after a pool is disabled for when it is
+re-enabled.
+- Icarus - correct MH/s and U: with work restart set at 8 seconds
+- ztex updateFreq was always reporting on fpga 0
+- Trying harder to get 1.15y working
+- Specifying threads on multi fpga boards extra cgpu
+- Missing the add cgpu per extra fpga on 1.15y boards
+- API add last share time to each pool
+- Don't try to reap curls if benchmarking is enabled.
+
+
+Version 2.4.0 - May 3, 2012
+
+- Only show longpoll warning once when it has failed.
+- Convert hashes to an unsigned long long as well.
+- Detect pools that have issues represented by endless rejected shares and
+disable them, with a parameter to optionally disable this feature.
+- Bugfix: Use a 64-bit type for hashes_done (miner_thread) since it can overflow
+32-bit on some FPGAs
+- Implement an older header fix for a label existing before the pthread_cleanup
+macro.
+- Limit the number of curls we recruit on communication failures and with
+delaynet enabled to 5 by maintaining a per-pool curl count, and using a pthread
+conditional that wakes up when one is returned to the ring buffer.
+- Generalise add_pool() functions since they're repeated in add_pool_details.
+- Bugfix: Return failure, rather than quit, if BFwrite fails
+- Disable failing devices such that the user can attempt to re-enable them
+- Bugfix: thread_shutdown shouldn't try to free the device, since it's needed
+afterward
+- API bool's and 1TBS fixes
+- Icarus - minimise code delays and name timer variables
+- api.c V1.9 add 'restart' + redesign 'quit' so thread exits cleanly
+- api.c bug - remove extra ']'s in notify command
+- Increase pool watch interval to 30 seconds.
+- Reap curls that are unused for over a minute. This allows connections to be
+closed, thereby allowing the number of curl handles to always be the minimum
+necessary to not delay networking.
+- Use the ringbuffer of curls from the same pool for submit as well as getwork
+threads. Since the curl handles were already connected to the same pool and are
+immediately available, share submission will not be delayed by getworks.
+- Implement a scaleable networking framework designed to cope with any sized
+network requirements, yet minimise the number of connections being reopened. Do
+this by create a ring buffer linked list of curl handles to be used by getwork,
+recruiting extra handles when none is immediately available.
+- There is no need for the submit and getwork curls to be tied to the pool
+struct.
+- Do not recruit extra connection threads if there have been connection errors
+to the pool in question.
+- We should not retry submitting shares indefinitely or we may end up with a
+huge backlog during network outages, so discard stale shares if we failed to
+submit them and they've become stale in the interim.
+
+
+Version 2.3.6 - April 29, 2012
+
+- Shorten stale share messages slightly.
+- Protect the freeing of current_hash under mutex_lock to prevent racing on it
+when set_curblock is hit concurrently.
+- Change default behaviour to submitting stale, removing the --submit-stale
+option and adding a --no-submit-stale option.
+- Make sure to start the getwork and submit threads when a pool is added on the
+fly. This fixes a crash when a pool is added to running cgminer and then
+switched to.
+- Faster hardware can easily outstrip the speed we can get work and submit
+shares when using only one connection per pool.
+- Test the queued list to see if any get/submits are already queued and if they
+are, start recruiting extra connections by generating new threads.
+- This allows us to reuse network connections at low loads but recuit new open
+connections as they're needed, so that cgminer can scale to hardware of any
+size.
+
+
+Version 2.3.5 - April 28, 2012
+
+- Restarting cgminer leads to a socket that can't be bound for 60 seconds, so
+increase the interval that API binding waits to 30 seconds to minimise the
+number of times it will retry, spamming the logs.
+- Give a longpoll message for any longpoll that detects a block change, primary
+or backup, and also display which pool it was.
+- Decrease utility display to one decimal place.
+- Small cosmetic output alignment.
+- Add pool number to stale share message.
+- Add space to log output now that there is more screen real estate available.
+- Indentation clean up.
+- Merge branch 'master' of github.com:ckolivas/cgminer
+- Remove thread id display from rejected shares as well.
+- Merge pull request #185 from Diapolo/diakgcn
+- add goffset support for diakgcn with -v 1 and update kernel version
+- Set have_longpoll to true when there is at least one pool with longpoll.
+- Don't display the thread ID since it adds no useful information over the
+device number.
+- Don't display the first 8 bytes of a share since they will always be zero at
+>= 1 difficulty.
+- work->longpoll is reset across test_work_current so we need to recheck what
+pool it belongs to.
+- Use longpolls from backup pools with failover-only enabled just to check for
+block changes, but don't use them as work.
+- Start longpoll only after we have tried to extract the longpoll URL.
+- Check for submitold flag on resubmit of shares, and give different message for
+stale shares on retry.
+- Check for submitold before submitstale.
+- Don't force fresh curl connections on anything but longpoll threads.
+- Create one longpoll thread per pool, using backup pools for those pools that
+don't have longpoll.
+- Use the work created from the longpoll return only if we don't have
+failover-enabled, and only flag the work as a longpoll if it is the current
+pool.
+- This will work around the problem of trying to restart the single longpoll
+thread on pool changes that was leading to race conditions.
+- It will also have less work restarts from the multiple longpolls received from
+different pools.
+- Remove the ability to disable longpoll. It is not a useful feature and will
+conflict with planned changes to longpoll code.
+- Remove the invalid entries from the example configuration file.
+- Add support for latest ATI SDK on windows.
+- Export missing function from libztex.
+- miner.php change socktimeoutsec = 10 (it only waits once)
+- Bugfix: Make initial_args a const char** to satisfy exec argument type warning
+(on Windows only)
+- miner.php add a timeout so you don't sit and wait ... forever
+- Create discrete persistent submit and get work threads per pool, thus allowing
+all submitworks belonging to the same pool to reuse the same curl handle, and
+all getworks to reuse their own handle.
+- Use separate handles for submission to not make getwork potentially delay
+share submission which is time critical.
+- This will allow much more reusing of persistent connections instead of opening
+new ones which can flood routers.
+- This mandated a rework of the extra longpoll support (for when pools are
+switched) and this is managed by restarting longpoll cleanly and waiting for a
+thread join.
+- miner.php only show the current date header once
+- miner.php also add current time like single rig page
+- miner.php display rig 'when' table at top of the multi-rig summary page
+- README - add some Ztex details
+- api.c include zTex in the FPGA support list
+- api.c ensure 'devs' shows PGA's when only PGA code is compiled
+- cgminer.c sharelog code consistency and compile warning fix
+- README correct API version number
+- README spelling error
+- api.c combine all pairs of sprintfs()
+- api.c uncomment and use BLANK (and COMMA)
+- Code style cleanup
+- Annotating frequency changes with the changed from value
+- README clarification of 'notify' command
+- README update for API RPC 'devdetails'
+- api.c 'devdetails' list static details of devices
+- Using less heap space as my TP-Link seems to not handle this much
+
+
+Version 2.3.4 - April 25, 2012
+
+- Extensively document the cause of GPU device issues and the use of --gpu-map.
+- Support for share logging
+- Detect poorly performing combination of SDK and phatk kernel and add verbose
+warning at startup.
+- Icarus update to new add_cgpu()
+- Icarus driver working with Linux and Windows
+- api.c fix unused variable compile warning
+- Display all OpenCL devices when -n is called as well to allow debugging of
+differential mapping of OpenCL to ADL.
+- Add a --gpu-map option which will allow arbitrarily mapping ADL devices to
+OpenCL devices for instances where association by enumeration alone fails.
+- Increase upper limit on number of extra items to queue as some FPGA code can't
+yet reliably keep many devices busy.
+- Display configuration file information when -c option is passed and only when
+file exists on loading default config file.
+- Display configuration file loaded, if any, and debug output if configuration
+file parsing failed.
+- Add missing ztex header to Makefile for distribution.
+- Document long-form COM port device names on Windows, required to specify
+serial ports above 9
+- Include ztex bitstreams firmware in distribution and install if configured in.
+- Style police on driver-ztex.c
+- work_restart should only be changed by cgminer.c now
+- Shut down the api cleanly when the api thread is cancelled. This should allow
+the api socket to be closed successfully to next be reopened with app_restart.
+- Make a union for cgpu device handles, and rename "device" to "device_ztex"
+since it's Ztex-specific
+- Initialise name variable.
+- Remove unnecessary check for variable that always has memory allocated.
+- Bugfix: Missing "break" no-op in default case
+- Make the status window and log window as large as can fit on startup,
+rechecking to see if it can be enlarged after the fact. This allows any number
+of devices to be displayed provided the window is made long enough without
+corrupting the output.
+- Style police on libztex.c.
+- API add removepool like the screen interface
+- api.c escape required characters in return strings + pools returns the
+username
+- Set lp_path to NULL after free for consistency.
+- Removing dmalloc import left behind by mistake
+- Fixing leak in resp_hdr_cb
+- miner.php warning highlight GPU stats if they are zero (e.g. ADL not enabled)
+- miner.php highlight any device that isn't 'Enabled'
+- miner.php highlight any Status that isn't 'Alive'
+- miner.php optionally support multiple rigs
+- Initial Ztex support 1.15x board.
+
+
+Version 2.3.3 - April 15, 2012
+
+- Don't even display that cpumining is disabled on ./configure to discourage
+people from enabling it.
+- Do a complete cgminer restart if the ATI Display Library fails, as it does on
+windows after running for some time, when fanspeed reporting fails.
+- Cache the initial arguments passed to cgminer and implement an attempted
+restart option from the settings menu.
+- Disable per-device status lines when there are more than 8 devices since
+screen output will be corrupted, enumerating them to the log output instead at
+startup.
+- Reuse Vals[] array more than W[] till they're re-initialised on the second
+sha256 cycle in poclbm kernel.
+- Minor variable alignment in poclbm kernel.
+- Make sure to disable devices with any status not being DEV_ENABLED to ensure
+that thermal cutoff code works as it was setting the status to DEV_RECOVER.
+- Re-initialising ADL simply made the driver fail since it is corruption over
+time within the windows driver that's responsible. Revert "Attempt to
+re-initialise ADL should a device that previously reported fanspeed stops
+reporting it."
+- Microoptimise poclbm kernel by ordering Val variables according to usage
+frequency.
+
+
+Version 2.3.2 - March 31, 2012
+
+- Damping small changes in hashrate so dramatically has the tendency to always
+make the hashrate underread so go back to gentle damping instead.
+- Revert the crossover of variables from Vals to W in poclbm kernel now that
+Vals are the first declared variables so they're used more frequently.
+- Vals variables appearing first in the array in poclbm is faster.
+- Change the preferred vector width to 1 for Tahiti only, not all poclbm
+kernels.
+- Use a time constant 0.63 for when large changes in hashrate are detected to
+damp change in case the large change is an aliasing artefact instead of a real
+chang
+- Only increment stale counter if the detected stales are discarded.
+- Attempt to re-initialise ADL should a device that previously reported fanspeed
+stops reporting it.
+- Move the ADL setup and clearing to separate functions and provide a reinit_adl
+function to be used when adl fails while running.
+- Use slightly more damping on the decay time function in the never-ending quest
+to smooth off the hashmeter.
+- Set the starting fanspeed to a safe and fairly neutral 50% when autofan is
+enabled.
+- Provide locking around updates of cgpu hashrates as well to prevent multiple
+threads accessing data fields on the same device.
+- Display the beginning of the new block in verbose mode in the logs.
+- Reinstate old diablo kernel variable ordering from 120222, adding only goffset
+and vector size hint. The massive variable ordering change only helped one SDK
+on
+- Change the version number on the correct kernels.
+- api.c devicecode/osinfo incorrectly swapped for json
+- Add extensive instructions on how to make a native windows build.
+- Update version numbers of poclbm and diablo kernels as their APIs have also
+changed.
+- Use global offset parameter to diablo and poclbm kernel ONLY for 1 vector
+kernels.
+- Use poclbm preferentially on Tahiti now regardless of SDK.
+- Remove unused constant passed to poclbm.
+- Clean up use of macros in poclbm and use bitselect everywhere possible.
+- Add vector type hint to diablo kernel.
+- Add worksize and vector attribute hints to the poclbm kernel.
+- Spaces for non-aligned variables in poclbm.
+- More tidying of poclbm.
+- Swap Vals and W variables where they can overlap in poclbm.
+- More tidying of poclbm.
+- Tidy up first half of poclbm.
+- Clean up use of any() by diablo and poclbm kernels.
+- Minor variable symmetry changes in poclbm.
+- Put additions on separate lines for consistency in poclbm.
+- Consolidate last use of W11 into Vals4 in poclbm.
+- Change email due to SPAM
+- api.c miner.php add a '*' to the front of all notify counters - simplifies
+future support of new counters
+- miner.php add display 'notify' command
+- Small change to help arch's without processor affinity
+- Fix bitforce compile error
+- api.c notify should report disabled devices also - of course
+- API returns the simple device history with the 'notify' command
+- code changes for supporting a simple device history
+- api.c Report an OS string in config to help with device issues
+- api.c fix Log Interval - integer in JSON
+- api.c config 'Device Code' to show list of compiled devices + README
+- api.c increase buffer size close to current code allowable limit
+- removed 8-component vector support from kernel, as this is not supported in
+CGMINER anyway
+- forgot to update kernel modification date, fixed ;)
+- reordered an addition in the kernel, which results in less instructions used
+in the GPU ISA code for GCN
+- miner.php: option for readonly or check privileged access
+- Ignore reduntant-with-build options --disable-gpu, --no-adl, and --no-restart
+- miner.php: ereg_replace is DEPRECATED so use preg_replace instead
+- Make curses TUI support optional at compile-time.
+- Bugfix: AC_ARG_WITH provides withval instead of enableval
+- miner.php split devs output for different devices
+- api.c: correct error messages
+- icarus.c modify (regular) timeout warning to only be debug
+- icarus.c set the windows TODO timeout
+- Allow specifying a specific driver for --scan-serial
+- optimized nonce-check and output code for -v 2 and -v 4
+- Bugfix: Check for libudev header (not just library) in configure, and document
+optional dependency
+- Add API support for Icarus and Bitforce
+- Next API version is 1.4 (1.3 is current)
+- README/api.c add "When" the request was processed to STATUS
+- Bugfix: ZLX to read BitFORCE temp, not ZKX -.-
+- Use libudev to autodetect BitFORCE GPUs, if available
+- Use the return value of fan_autotune to set fan_optimal instead of passing it
+as a pointer.
+- Pass the lasttemp from the device we're using to adjust fanspeed in twin
+devices.
+- fix the name to 3 chars, fix the multi-icarus support
+- Bugfix: "-S auto" is the default if no -S is specified, and there is no such
+delay in using it
+- README add information missing from --scan-serial
+- Update README RPC API Version comment
+- Bugfix: Allow enabling CPU even without OpenCL support
+- Change failed-to-mine number of requested shares messge to avoid segfault on
+recursive calling of quit().
+- Get rid of extra char which is just truncated in poclbm kernel.
+- only small code formating changes
+- removed vec_step() as this could lead to errors on older SDKs
+- unified code for generating nonce in kernel and moved addition of base to the
+end -> faster
+
+Version 2.3.1 - February 24, 2012
+
+- Revert input and output code on diakgcn and phatk kernels to old style which
+worked better for older hardware and SDKs.
+- Add a vector*worksize parameter passed to those kernels to avoid one op.
+- Increase the speed of hashrate adaptation.
+- Only send out extra longpoll requests if we want longpolls.
+- API implement addpool command
+- API return the untouched Total MH also (API now version 1.3)
+- Add enable/disablepool to miner.php example and reduce font size 1pt
+
+
+Version 2.3.0 - February 23, 2012
+
+- Consider extra longpoll work items as staged_extra so as to make sure we queue
+more work if queueing regular work items as longpolls.
+- Use diablo kernel on all future SDKs for Tahiti and set preferred vector width
+to 1 on poclbm kernel only.
+- Explicitly type the constants in diakgcn kernel as uint, to be in line with
+poclbm kernel.
+- Reset all hash counters at the same time as resetting start times to get
+accurate hashrates on exiting which is mandatory for benchmarking.
+- Report thread out before it starts to avoid being flagged as sick when waiting
+for the first work item.
+- Don't disable and re-enable devices as they may recover and in the meantime
+have their status set to OFF.
+- API new commands enablepool and disablepool (version already incremented)
+- Tolerate new-format temperature readings for bitforce
+- Modify cgminer.c pool control to allow API to call it
+- Bugfix: Fix BitFORCE driver memory leak in debug logging
+- Extra byte was being unused in poclbm leading to failure on some platforms.
+- Explicitly type the constants in poclbm kernel as uint.
+- Don't save 'include' when saving the configuration
+- Allow configuration file to include another recursively
+- Use the SDK and hardware information to choose good performing default
+kernels.
+- Move phatk kernel to offset vector based nonce bases as well.
+- Add a --benchmark feature which works on a fake item indefinitely to compare
+device performance without any server or networking influence.
+- Allow writing of multiple worksizes to the configuration file.
+- Allow writing of multiple vector sizes to the configuration file.
+- Allow writing of multiple kernels to the configuration file.
+- Allow multiple different kernels to be chosen per device.
+- Allow the worksize to be set per-device.
+- Allow different vectors to be set per device.
+- If we're well below the target temperature, increase gpu engine speed back to
+maximum in case we have gotten lost between profiles during an idle period.
+- We should be setting the value of fan_optimal, not its address.
+- As all kernels will be new versions it's an opportunity to change the .bin
+format and make it simpler. Specifying bitalign is redundant and long can be l.
+- Use any() in kernel output code.
+- Put the nonce for each vector offset in advance, avoiding one extra addition
+in the kernel.
+- Reset times after all mining threads are started to make estimating hashrates
+easier at startup.
+- Bugfix: allow no-exec (NX) stack
+- Fix minor warning.
+- fix the bitforce.c code style follow 1TBS
+- fix icarus.c compile warning
+- small changes to speedup no vec for AMD 898.1 OCL runtime
+- Update licensing to GPL V3.
+- Reset the longpoll flag after it's been used once to prevent it restarting
+work again.
+- Begin import of DiabloMiner kernel.
+- Modify API debug messages to say API instead of DBG
+- When API shuts down cgminer don't kill itself
+- Don't make rolled work from the longpoll be seen as other longpoll work items.
+- API add 'privileged' command so can verify access level
+- Set the lp_sent variable under lock since there will almost always be a race
+on setting this variable, potentially leading to multiple LPs being sent out.
+- API restrict access to all non display commands by default
+- Update API version to 1.2 for new 'Log Interval'
+- API add --log Interval to 'config' reply
+- --api-allow special case 0/0 means all
+
+
+Version 2.2.7 - February 20, 2012
+
+- Send out extra longpolls when we have switched pools and the longpoll thread
+is still bound to the old one. This is particularly useful with p2pool where
+longpolls do not correlate with main bitcoin block change and would have led to
+high reject rates on failover.
+- Store whether a work item is the result of a longpoll or not in struct work
+and use it to help determine block changes directly from the work longpoll bool.
+- Keep track of when a longpoll has been sent for a pool and if the current pool
+is requesting work but has not sent a longpoll request, convert one of the work
+items to a longpoll.
+- Store the longpoll url in the pool struct and update it from the pool_active
+test in case it changes. This is to allow further changes to longpoll management
+on switching pools.
+- Re-check for a longpoll supporting pool every 30 seconds if none is found
+initially.
+- Report threads as busy waiting on getwork on startup to avoid them being
+flagged sick on startup during slow networking.
+- Allow devices that are disabled due to overheating to be flagged as recovering
+instead of disabling them and re-enable them if they're below ideal temperatures
+- Tahiti prefers worksize 64 with poclbm.
+- No need to expressly retain the opencl program now that the zero binary issue
+is fixed. This actually fixes cgminer to work with the latest SDK included with
+the ATI catalyst driver 12.2.
+- Show error code on any opencl failure status.
+- Add detection for version 898.1 SDK as well but only give SDK 2.6 warning once
+on startup instead of with each device initialisation.
+- Always use a fresh connection for longpoll as prolonged persistent connections
+can fail for many reasons.
+- Keep track of intended engine clock speed and only adjust up if it's higher
+than the last intended speed. This avoids setting the clock speed to one
+relative to a lower profile one by mistake.
+- Use gpu-memdiff on startup if an engine clockspeed is set and a memdiff value
+is set.
+- Revert "Adjust engine speed up according to performance level engine setting,
+not the current engine speed." - ineffectual.
+- Freeze the queues on all threads that are sent the pause message to prevent
+them trying to start up again with saved pings in their queues.
+- Updates to diakgcn kernel/
+- Consolidate all screen updates to the watchdog thread and touch both windows
+before refresh.
+- Curses will be disabled in clean_up so don't do it early in kill_work, and
+disable_adl so that GPU settings may be restored to normal in case shutting down
+curses leads to instability on windows.
+- Stop the mining threads before trying to kill them.
+- Plain refresh() does not give reliably screen updates so get rid of all uses
+of it.
+- First release with working diakgcn kernel.
+
+Version 2.2.6 - February 16, 2012
+
+- Provide warning on each startup about sdk 2.6
+- Fix unused warnings on win32.
+- bitforce: Simplify BFopen WIN32 ifdef/else
+- Fix initialization warning with jansson 1.3
+- bitforce: Cleanup extraneous TODO that isn't needed
+- Move tcsetattr (and new tcflush) into *nix BFopen to simplify things a bit
+- Add message explaining 2nd thread disabling for dynamic mode and how to tune
+it.
+- Move logwindow down once number of devices is known.
+- Automatically choose phatk kernel for bitalign non-gcn ATI cards, and then
+only select poclbm if SDK2.6 is detected.
+- Allow the refresh interval to be adjusted in dynamic intensity with a
+--gpu-dyninterval parameter.
+- Make curses display visible right from the beginning and fix the window sizes
+so the initial messages don't get lost once the status window is drawn.
+- The amount of work scanned can fluctuate when intensity changes and since we
+do this one cycle behind, we increment the work more than enough to prevent
+repeati
+- bitforce: Set a 30 second timeout for serial port on Windows, since the
+default is undefined
+- Use PreVal4addT1 instead of PreVal4 in poclbm kernel.
+- Import PreVal4 and PreVal0 into poclbm kernel.
+- Import more prepared constants into poclbm kernel.
+- Keep variables in one array but use Vals[] name for consistency with other
+kernel designs.
+- Replace constants that are mandatorily added in poclbm kernel with one value.
+- Remove addition of final constant before testing for result in poclbm kernel.
+- Hand optimise variable addition order.
+- Hand optimise first variable declaration order in poclbm kernel.
+- Radical reordering machine based first pass to change variables as late as
+possible, bringing their usage close together.
+- fix strcpy NULL pointer if env HOME unset.
+- bitforce: Disable automatic scanning when at least one device is specified
+manually
+- Unroll all poclbm additions to enable further optimisations.
+
+
+Version 2.2.5 - February 13, 2012
+
+- Make output buffer write only as per Diapolo's suggestion.
+- Constify nonce in poclbm.
+- Use local and group id on poclbm kernel as well.
+- Microoptimise phatk kernel on return code.
+- Adjust engine speed up according to performance level engine setting, not the
+current engine speed.
+- Try to load a binary if we've defaulted to the poclbm kernel on SDK2.6
+- Use the poclbm kernel on SDK2.6 with bitalign devices only if there is no
+binary available.
+- Further generic microoptimisations to poclbm kernel.
+- The longstanding generation of a zero sized binary appears to be due to the
+OpenCL library putting the binary in a RANDOM SLOT amongst 4 possible binary
+locations. Iterate over each of them after building from source till the real
+binary is found and use that.
+- Fix harmless warnings with -Wsign-compare to allow cgminer to build with -W.
+- Fix missing field initialisers warnings.
+- Put win32 equivalents of nanosleep and sleep into compat.h fixing sleep() for
+adl.c.
+- Restore compatibility with Jansson 1.3 and 2.0 (api.c required 2.1)
+- Modularized logging, support for priority based logging
+- Move CPU chipset specific optimization into device-cpu
+
+
+Version 2.2.4 - February 11, 2012
+
+- Fix double definition of A0 B0 to zeroA zeroB.
+- Retain cl program after successfully loading a binary image. May decrease
+failures to build kernels at startup.
+- Variable unused after this so remove setting it.
+- BFI INT patching is not necessarily true on binary loading of files and not
+true on ATI SDK2.6+. Report bitalign instead.
+- Various string fixes for reject reason.
+- Generalize --temp-cutoff and implement support for reading temperature from
+BitFORCE FPGAs
+- Change message from recovered to alive since it is used on startup as well as
+when a pool has recovered.
+- Start mining as soon as any pool is found active and rely on the watchpool
+thread to bring up other pools.
+- Delayed responses from testing pools that are down can hold up the watchdog
+thread from getting to its device testing code, leading to false detection of
+the GPU not checking in, and can substantially delay auto gpu/auto fan
+management leading to overheating. Move pool watching to its own thread.
+- Bugfix: BitFORCE index needs to be static to count correctly
+- Space out retrieval of extra work according to the number of mining threads.
+- Make shutdown more robust. Enable the input thread only after the other
+threads exist. Don't kill off the workio thread and use it to exit main() only
+if there is an unexpected problem. Use kill_work() for all anticipated shutdowns
+where possible. Remove unused thread entry.
+- Change poclbm version number.
+- One array is faster than 2 separate arrays so change to that in poclbm kernel.
+- Microoptimisations to poclbm kernel which increase throughput slightly.
+- Import diablominer kernel. Currently disabled as not working.
+- Import diapolo kernel. Currently disabled as not working.
+- Conflicting entries of cl_kernel may have been causing problems, and
+automatically chosen kernel type was not being passed on. Rename the enum to
+cl_kernels and store the chosen kernel in each clState.
+- Set cl_amd_media_ops with the BITALIGN flag and allow non-bitselect devices to
+build.
+- ALlow much longer filenames for kernels to load properly.
+- Allow different kernels to be used by different devices and fix the logic fail
+of overcorrecting on last commit with !strstr.
+- Fix kernel selection process and build error.
+- queue_phatk_kernel now uses CL_SET_VARG() for base-nonce(s), too
+- added OpenCL >= 1.1 detection code, in preparation of OpenCL 1.1 global offset
+parameter support
+- Use K array explicitly to make it clear what is being added.
+- Work items have a tendency to expire at exactly the same time and we don't
+queue extra items when there are plenty in the queue, regardless of age. Allow
+extra work items to be queued if adequate time has passed since we last
+requested work even if over the limit.
+- Discard work when failover-only is enabled and the work has come from a
+different pool.
+- Missing include to build on newer mingw32.
+- Move from the thread safe localtime_r to regular localtime which is the only
+one supported on newer pthread libraries on mingw32 to make it compile with the
+newer ming. Thread safety is of no importance where localtime is used in this
+code.
+- Define in_addr_t in windows if required
+- sys/wait.h not required in windows
+- Allow API to restrict access by IP address
+- Add pool switching to example miner.php
+- Display X-Reject-Reason, when provided
+- Remove the test for whether the device is on the highest profil level before
+raising the GPU speed as it is ineffectual and may prevent raising the GPU
+speed.
+- Remove unnecessary check for opt_debug one every invocation of applog at
+LOG_DEBUG level and place the check in applog().
+
+
+Version 2.2.3 - February 6, 2012
+
+- Revert "Rewrite the convoluted get_work() function to be much simpler and roll
+work as much as possible with each new work item." This seems to cause a race on
+work in free_work(). Presumably other threads are still accessing the structure.
+
+
+Version 2.2.2 - February 6, 2012
+
+- Provide support for the submitold extension on a per-pool basis based on the
+value being detected in a longpoll.
+- Don't send a ping to a dynamic device if it's not enabled as that will just
+enable it for one pass and then disable it again.
+- Rewrite the convoluted get_work() function to be much simpler and roll work as
+much as possible with each new work item.
+- Roll as much work as possible from the work returned from a longpoll.
+- Rolling work on each loop through the mining thread serves no purpose.
+- Allow to stage more than necessary work items if we're just rolling work.
+- Replace divide_work with reuse_work function used twice.
+- Give rolled work a new ID to make sure there is no confusion in the hashtable
+lookups.
+- Remove now-defunct hash_div variables.
+- Remove unused get_dondata function.
+- Silence ADL warnings.
+- Silence unused parameter warnings.
+- Stagger the restart of every next thread per device to keep devices busy ahead
+of accessory threads per device.
+- Deprecate the --donation feature. Needlessly complex, questionable usefulness,
+depends on author's server and a central pool of some kind, and was not heavily
+adopted.
+- It's devices that report back now, not threads, update message.
+- Continue auto-management of fan and engine speeds even if a device is disabled
+for safety reasons.
+- No need to check we're highest performance level when throttling GPU engine
+speed.
+- Abstract out tests for whether work has come from a block that has been seen
+before and whether a string is from a previously seen block.
+- Probe but don't set the timeout to 15 seconds as some networks take a long
+time to timeout.
+- Remove most compiler warnings from api.c
+- Add last share's pool info in cgpu_info
+- Allow the OpenCL platform ID to be chosen with --gpu-platform.
+- Iterate over all platforms displaying their information and number of devices
+when --ndevs is called.
+- Deprecate main.c
+- Some networks can take a long time to resolve so go back to 60 second timeouts
+instead of 15.
+- Only enable curses on failure if curses is desired.
+- Fix warnings in bitforce.c
+- Bugfix: Need to open BitForce tty for read-write
+- Fix various build issues.
+- Modularize code: main.c -> device-cpu + device-gpu
+- Fix phatk kernel not working on non-bitalign capable devices (Nvidia, older
+ATI).
+- Update poclbm kernel for better performance on GCN and new SDKs with bitalign
+support when not BFI INT patching. Update phatk kernel to work properly for non
+BFI INT patched kernels, providing support for phatk to run on GCN and non-ATI
+cards.
+- Return last accepted share pool/time for devices
+- Display accepted share pool/time for CPUs
+- Bug intensity always shows GPU 0
+- Update example web miner.php to use new API commands
+
+
+Version 2.2.1 - January 30, 2012
+
+NOTE - The GPU Device reordering in 2.2.0 by default was considered a bad idea
+so the original GPU ordering is used by default again unless reordering is
+explicitly requested.
+
+- Fix bitforce failing to build into cgminer.
+- Add missing options to write config function.
+- Add a --gpu-reorder option to only reorder devices according to PCI Bus ID
+when requested.
+- Fix for midstate support being broken on pools that supported no-midstate
+work by ensuring numbers are 32 bits in sha2.c
+- Set virtual GPUs to work when ADL is disabled or all mining will occur on GPU
+0.
+- Add information about paused threads in the menu status.
+- Disable all but the first thread on GPUs in dynamic mode for better
+interactivity.
+- Set the latest network access time on share submission for --net-delay even if
+we're not delaying that submission for further network access.
+- Clear adl on exiting after probing values since it may attempt to overclock.
+- As share submission is usually staggered, and delays can be costly, submit
+shares without delay even when --net-delay is enabled.
+- Display GPU number and device name when ADL is successfully enabled on it.
+- Display GPU ordering remapping in verbose mode.
+- Don't fail in the case the number of ADL and OpenCL devices do not match, and
+do not attempt to reorder devices unless they match. Instead give a warning
+about
+- Display error codes should ADL not return ADL_OK in the more critical function
+calls.
+- Fix unused warning.
+- Fix compile warnings in api.c
+- Add extensive ADL based device info in debug mode.
+- Make --ndevs display verbose opencl information as well to make debugging
+version information easier.
+- Display information about the opencl platform with verbose enabled.
+- Explicitly check for nvidia in opencl platform strings as well.
+
+
+Version 2.2.0 - January 29, 2012
+
+NOTE: GPU Device order will change with this release with ATI GPUs as cgminer
+now can enumerate them according to their Bus ID which means the values should
+now correlate with their physical position on the motherboard.
+
+- Default to poclbm kernel on Tahiti (7970) since phatk does not work, even
+though performance is sub-standard so that at least it will mine successfully by
+defau
+- Retain cl program after every possible place we might build the program.
+- Update ADL SDK URL.
+- Fix potential overflow.
+- Map GPU devices to virtual devices in their true physical order based on
+BusNumber.
+- Change the warning that comes with failure to init cl on a device to be more
+generic and accurate.
+- Advertise longpoll support in X-Mining-Extensions
+- Detect dual GPU cards by iterating through all GPUs, finding ones without
+fanspeed and matching twins with fanspeed one bus ID apart.
+- Do not attempt to build the program that becomes the kernel twice. This could
+have been leading to failures on initialising cl.
+- Some opencl compilers have issues with no spaces after -D in the compiler
+options.
+- Allow intensity up to 14.
+- Use calloced stack memory for CompilerOptions to ensure sprintf writes to the
+beginning of the char.
+- Whitelist 79x0 cards to prefer no vectors as they perform better without.
+- Adjust fan speed gently while in the optimal range when temperature is
+drifting to minimise overshoot in either direction.
+- Detect dual GPU cards via the indirect information of - 1st card has a fan
+controller. 2nd card does not have a fan controller, cards share the same device
+name
+- Instead of using the BFI_INT patching hack on any device reporting
+cl_amd_media_ops, create a whitelist of devices that need it. This should enable
+GCN architec
+- Fixed API compiling issue on OS X
+- Add more explanation of JSON format and the 'save' command
+- Return an error if using ADL API commands when it's not available
+- Read off lpThermalControllerInfo from each ADL device.
+- Add ADL_Overdrive5_ThermalDevices_Enum interface.
+- Add API commands: config, switchpool, gpu settings, save
+- Implement socks4 proxy support.
+- Fix send() for JSON strings
+- Introduce a --net-delay option which guarantees at least 250ms between any
+networking requests to not overload slow routers.
+- Generalise locking init code.
+- Allow invalid values to be in the configuration file, just skipping over them
+provided the rest of the file is valid JSON. This will allow older configurat
+- Allow CPU mining explicitly enable only if other mining support is built in.
+- BitForce FPGA support
+- Configure out building and support of all CPU mining code unless
+--enable-cpumining is enabled.
+- Allow parsed values to be zero which will allow 0 values in the config file to
+work.
+- Advertise that we can make our own midstate, so the pool can skip generating
+it for us
+- Refactor the CPU scanhash_* functions to use a common API. Fixes bugs.
+- Don't consider a pool lagging if a request has only just been filed. This
+should decrease the false positives for "pool not providing work fast enough".
+- Invalidating work after longpoll made hash_pop return no work giving a false
+positive for dead pool. Rework hash_pop to retry while finds no staged work u
+- Remove TCP_NODELAY from curl options as many small packets may be contributing
+to network overload, when --net-delay is enabled.
+- Refactor miner_thread to be common code for any kind of device
+- Simplify submit_nonce loop and avoid potentially missing FOUND - 1 entry.
+Reported by Luke-Jr.
+- Micro-optimisation in sha256_sse2 code courtesy of Guido Ascioti
+guido.ascioti@gmail.com
+- Refactor to abstract device-specific code
+
+
+Version 2.1.2 - January 6, 2012
+
+- If api-description is specified, save it when writing the config file
+- Adjust utility width to be constant maximum as well.
+- Add percent signs to reject ratio outputs
+- Should the donation pool fail, don't make the fallover pool behave as though
+the primary pool is lagging.
+- Use an alternative pool should the donation getwork fail.
+
+
+Version 2.1.1 - January 1, 2012
+
+- Include API examples in distribution tarball.
+- Don't attempt to pthread_join when cancelling threads as they're already
+detached and doing so can lead to a segfault.
+- Give more generic message if slow pool at startup is the donation pool.
+- Continue to attempt restarting GPU threads if they're flagged dead at 1 min.
+intervals.
+- Don't attempt to restart sick flagged GPUs while they're still registering
+activity.
+- Make curl use fresh connections whenever there is any communication issue
+in case there are dead persistent connections preventing further comms from
+working.
+- Display pool in summary if only 1 pool.
+- Adjust column width of A/R/HW to be the maximum of any device and align them.
+
+
+Version 2.1.0 - December 27, 2011
+
+- Major infrastructure upgrade with RPC interface for controlling via sockets
+encoded with/without JSON courtesy of Andrew Smith. Added documentation for
+use of the API and sample code to use with it.
+- Updated linux-usb-cgminer document.
+- Rewrite of longpoll mechanism to choose the current pool wherever possible to
+use for the longpoll, or any pool that supports longpoll if the current one
+does not.
+- Display information about longpoll when the chosen server has changed.
+- Fix the bug where longpoll generated work may have been sent back to the
+wrong pool, causing rejects.
+- Fix a few race conditions on closing cgminer which caused some of the crashes
+on exit.
+- Only adjust gpu engine speed in autotune mode if the gpu is currently at the
+performance level of that being adjusted.
+- Various fixes for parsing/writing of configuration files.
+- Do not add blank lines for threads of unused CPUs.
+- Show which pool is unresponsive on startup.
+- Only show GPU management menu item if GPUs are in use.
+- Align most device columns in the curses display.
+
+
+Version 2.0.8 - November 11, 2011
+
+- Make longpoll do a mandatory flushing of all work even if the block hasn't
+changed, thus supporting longpoll initiated work change of any sort and merged
+mining.
+- Byteswap computed hash in hashtest so it can be correctly checked. This fixes
+the very rare possibility that a block solve on solo mining was missed.
+- Add x86_64 w64 mingw32 target
+- Allow a fixed speed difference between memory and GPU clock speed with
+--gpu-memdiff that will change memory speed when GPU speed is changed in
+autotune mode.
+- Don't load the default config if a config file is specified on the command
+line.
+- Don't build VIA on apple since -a auto bombs instead of gracefully ignoring
+VIA failing.
+- Build fix for dlopen/dlclose errors in glibc.
+
+
+Version 2.0.7 - October 17, 2011
+
+- Support work without midstate or hash1, which are deprecated in bitcoind 0.5+
+- Go to kernel build should we fail to clCreateProgramWithBinary instead of
+failing on that device. This should fix the windows problems with devices not
+initialising.
+- Support new configuration file format courtesy of Chris Savery which can write
+the config file from the menu and will load it on startup.
+- Write unix configuration to .cgminer/cgminer.conf by default and prompt to
+overwrite if given a filename from the menu that exists.
+
+
+Version 2.0.6 - October 9, 2011
+
+- Must initialise the donorpool mutex or it fails on windows.
+- Don't make donation work interfere with block change detection allowing
+donation to work regardless of the block chain we're mining on.
+- Expire shares as stale with a separate timeout from the scantime, defaulting
+to 120 seconds.
+- Retry pools after a delay of 15 seconds if none can be contacted on startup
+unless a key is pressed.
+- Don't try to build adl features without having adl.
+- Properly check shares against target difficulty - This will no longer show
+shares when solo mining at all unless they're considered to be a block solve.
+- Add altivec 4 way (cpu mining) support courtesy of Gilles Risch.
+- Try to use SSL if the server supports it.
+- Display the total solved blocks on exit (LOL if you're lucky).
+- Use ADL activity report to tell us if a sick GPU is still busy suggesting it
+is hard hung and do not attempt to restart it.
+
+
+Version 2.0.5 - September 27, 2011
+
+- Intensity can now be set to dynamic or static values per-device.
+- New donation feature --donation sends a proportion of shares to author's
+account of choice, but is disabled by default!
+- The hash being displayed and block detection has been fixed.
+- Devices not being mined on will not attempt to be ADL managed.
+- Intensity is now displayed per GPU device.
+- Make longpoll attempt to restart as often as opt_retries specifies.
+- We weren't rolling work as often as we could.
+- Correct some memory management issues.
+- Build fixes.
+- Don't mess with GPUs if we don't have them.
+
+
+Version 2.0.4 - September 23, 2011
+
+- Confused Longpoll messages should be finally fixed with cgminer knowing for
+sure who found the new block and possibly avoiding a rare crash.
+- Display now shows the actual hash and will say BLOCK! if a block is deemed
+solved.
+- Extra spaces, which would double space lines on small terminals, have been
+removed.
+- Fan speed change is now damped if it is already heading in the correct
+direction to minimise overshoot.
+- Building without opencl libraries is fixed.
+- GPUs are autoselected if there is only one when in the GPU management menu.
+- GPU menu is refreshed instead of returning to status after a GPU change.
+
+
+Version 2.0.3 - September 17, 2011
+
+- Various modes of failure to set fanspeeds and adl values have been addressed
+and auto-fan should work now on most hardware, and possibly other values
+which previously would not have worked.
+- Fixed a crash that can occur on switching pools due to longpoll thread races.
+- Use ATISTREAMSDKROOT if available at build time.
+- Fanspeed management is returned to the driver default on exit instead of
+whatever it was when cgminer was started.
+- Logging of events deemed WARNING or ERR now will display even during
+periods where menu input is being awaited on.
+
+
+Version 2.0.2 - September 11, 2011
+
+- Exit cleanly if we abort before various threads are set up or if they no
+longer exist.
+- Fix a rare crash in HASH_DEL due to using different mutexes to protect the
+data.
+- Flag devices that have never started and don't allow enabling of devices
+without restarting them.
+- Only force the adapter speed to high if we've flagged this device as being
+managed.
+- Flag any devices with autofan or autogpu as being managed.
+- Use a re-entrant value to store what fanspeed we're trying to set in case the
+card doesn't support small changes.     Force it to a multiple of 10% if it
+fails on trying to speed up the fan.
+- Do not bother resetting values to old ones if changes to GPU parameters report
+failure, instead returning a failure code only if the return value from get()
+differs.
+- Remove redundant check.
+- Only display supported values from fanspeed on change settings.
+- Missing bracket from output.
+- Display fan percentage on devices that only support reporting percent and not
+RPM.
+- Properly substitute DLOPEN flags to build with ADL support when -ldl is needed
+and not when opencl is not found.
+
+
+Version 2.0.1 - September 9, 2011
+
+- Fix building on 32bit glibc with dlopen with -lpthread and -ldl
+- ByteReverse is not used and the bswap opcode breaks big endian builds. Remove
+it.
+- Ignore whether the display is active or not since only display enabled devices
+work this way, and we skip over repeat entries anwyay.
+- Only reset values on exiting if we've ever modified them.
+- Flag adl as active if any card is successfully activated.
+- Add a thermal cutoff option as well and set it to 95 degrees by default.
+- Change the fan speed by only 5% if it's over the target temperature but less
+than the hysteresis value to minimise overshoot down in temperature.
+- Add a --no-adl option to disable ADL monitoring and GPU settings.
+- Only show longpoll received delayed message at verbose level.
+- Allow temperatures greater than 100 degrees.
+- We should be passing a float for the remainder of the vddc values.
+- Implement accepting a range of engine speeds as well to allow a lower limit to
+be specified on the command line.
+- Allow per-device fan ranges to be set and use them in auto-fan mode.
+- Display which GPU has overheated in warning message.
+- Allow temperature targets to be set on a per-card basis on the command line.
+- Display fan range in autofan status.
+- Setting the hysteresis is unlikely to be useful on the fly and doesn't belong
+in the per-gpu submenu.
+- With many cards, the GPU summaries can be quite long so use a terse output
+line when showing them all.
+- Use a terser device status line to show fan RPM as well when available.
+- Define max gpudevices in one macro.
+- Allow adapterid 0 cards to enumerate as a device as they will be non-AMD
+cards, and enable ADL on any AMD card.
+- Do away with the increasingly confusing and irrelevant total queued and
+efficiency measures per device.
+- Only display values in the log if they're supported and standardise device log
+line printing.
+
+
+Version 2.0.0 - September 6, 2011
+
+Major feature upgrade - GPU monitoring, (over)clocking and fan control for ATI
+GPUs.
+
+New command line switches:
+--auto-fan-     Automatically adjust all GPU fan speeds to maintain a target
+temperature
+--auto-gpu-     Automatically adjust all GPU engine clock speeds to maintain
+a target temperature
+--gpu-engine <arg>  Set the GPU engine (over)clock in Mhz - one value for all or
+separate by commas for per card.
+--gpu-fan <arg>     Set the GPU fan percentage - one value for all or separate
+by commas for per card.
+--gpu-memclock <arg> Set the GPU memory (over)clock in Mhz - one value for all
+or separate by commas for per card.
+--gpu-powertune <arg> Set the GPU powertune percentage - one value for all or
+separate by commas for per card.
+--gpu-vddc <arg>    Set the GPU voltage in Volts - one value for all or separate
+by commas for per card.
+--temp-hysteresis <arg> Set how much the temperature can fluctuate outside
+limits when automanaging speeds (default: 3)
+--temp-overheat <arg> Set the overheat temperature when automatically managing
+fan and GPU speeds (default: 85)
+--temp-target <arg> Set the target temperature when automatically managing fan
+and GPU speeds (default: 75)
+
+- Implement ATI ADL support for GPU parameter monitoring now and setting later
+(temp, fan, clocks etc.).
+- Check for the presence of the ADL header files in ADL_SDK.
+- Import adl_functions.h from amd overdrive ctrl.
+- Implement a setup function that tries to detect GPUs that support the ADL and
+link in the parameters into the gpus struct.
+- Put a summary of monitoring information from the GPU menu.
+- Implement changing memory speed and voltage on the fly.
+- Implement fan speed setting.
+- Minor corrections to set fan speed by percentage.
+- Make sure to read off the value in RPM only.
+- Implement auto fanspeed adjustment to maintain a target temperature and
+fanspeed below 85%, with an overheat check that will speed the fan up to 100%.
+- Add an --auto-fan command line option to allow all GPUs to have autofan
+enabled from startup.
+- Add a gpu autotune option which adjusts GPU speed to maintain a target
+temperature within the bounds of the default GPU speed and any overclocking set.
+- Avoid a dereference if the longpoll thread doesn't exist.
+- Clean up by setting performance profiles and fan settings to startup levels on
+exit.
+- Add a small amount of hysteresis before lowering clock speed.
+- Allow target, overheat and hysteresis temperatures to be set from command
+line.
+- Combine all stats collating into one function to avoid repeating function
+calls on each variable.
+- Add gpu statistics to debugging output via the watchdog thread.
+- Implement menus to change temperature limits.
+- Implement setting the GPU engine clock speed of all devices or each device as
+a comma separated value.
+- Implement setting the GPU memory clock speed of all devices or each device as
+a comma separated value.
+- Implement setting the GPU voltage of all devices or each device as a comma
+separated value.
+- Implement setting the GPU fan speed of all devices or each device as a comma
+separated value.
+- Add support for monitoring powertune setting.
+- Implement changing of powertune value from the GPU change settings menu.
+- Get the value of powertune in get_stats.
+- Implement setting the GPU powertune value of all devices or each device as a
+comma separated value.
+- Remove the safety checks in speed setting since confirmation is done first in
+the menu, then show the new current values after a short pause.
+- Force the speed to high on startup and restore it to whatever the setting was
+on exit.
+- Add temperature to standard output where possible and use more compact output.
+- Move and print at the same time in curses to avoid random trampling display
+errors.
+- Update the status window only from the watchdog thread, do not rewrite the top
+status messages and only refresh once all the status window is complete,
+clearing the window each time to avoid corruption.
+- Set a safe starting fan speed if we're automanaging the speeds.
+- Provide locking around all adl calls to prevent races.
+- Lower profile settings cannot be higher than higher profile ones so link any
+drops in settings.
+- Add new needed text files to distribution.
+- Queue requests ignoring the number of staged clones since they get discarded
+very easily leading to false positives for pool not providing work fast enough.
+- Include libgen.h in opt.c to fix win32 compilation warnings.
+- Fix compilation warning on win32.
+- Add the directory name from the arguments cgminer was called from as well to
+allow it running from a relative pathname.
+- Add a --disable-adl option to configure and only enable it if opencl support
+exists.
+- Retry before returning a failure to get upstream work as a failure to avoid
+false positives for pool dead.
+- Retry also if the decoding of work fails.
+- Use the presence of X-Roll-Ntime in the header as a bool for exists unless N
+is found in the response.
+
+
+Version 1.6.2 - September 2, 2011
+
+- Add --failover-only option to not leak work to backup pools when the primary
+pool is lagging.
+- Change recommendation to intensity 9 for dedicated miners.
+- Fix the bouncing short term value by allowing it to change dynamically when
+the latest value is very different from the rolling value, but damp the change
+when it gets close.
+- Use the curses_lock to protect the curses_active variable and test it under
+lock.
+- Go back to requesting work 2/3 of the way through the current scantime with
+CPU mining as reports of mining threads running out of work have occurred with
+only 5 seconds to retrieve work.
+- Add start and stop time scheduling for regular time of day running or once off
+start/stop options.
+- Print summary on quit modes.
+- Put some sanity checks on the times that can be input.
+- Give a verbose message when no active pools are found and pause before
+exiting.
+- Add verbose message when a GPU fails to initialise, and disable the correct
+GPU.
+- Cryptopp asm32 was not correctly updated to the incremental nonce code so the
+hash counter was bogus.
+- Get rid of poorly executed curl check.
+- If curl does not have sockopts, do not try to compile the
+json_rpc_call_sockopt_cb function, making it possible to build against older
+curl libraries.
+- Most people expect /usr/local when an unspecified prefix is used so change to
+that.
+- Rename localgen occasions to getwork fail occasions since localgen is
+unrelated now.
+
+
+Version 1.6.1 - August 29, 2011
+
+- Copy cgminer path, not cat it.
+- Switching between redrawing windows does not fix the crash with old
+libncurses, so redraw both windows, but only when the window size hasn't
+changed.
+- Reinstate minimum 1 extra in queue to make it extremely unlikely to ever have
+0 staged work items and any idle time.
+- Return -1 if no input is detected from the menu to prevent it being
+interpreted as a 0.
+- Make pthread, libcurl and libcurses library checks mandatory or fail.
+- Add a --disable-opencl configure option to make it possible to override
+detection of opencl and build without GPU mining support.
+- Confusion over the variable name for number of devices was passing a bogus
+value which likely was causing the zero sized binary issue.
+- cgminer no longer supports default url user and pass so remove them.
+- Don't show value of intensity since it's dynamic by default.
+- Add options to explicitly enable CPU mining or disable GPU mining.
+- Convert the opt queue into a minimum number of work items to have queued
+instead of an extra number to decrease risk of getting idle devices without
+increasing risk of higher rejects.
+- Statify tv_sort.
+- Check for SSE2 before trying to build 32 bit SSE2 assembly version. Prevents
+build failure when yasm is installed but -msse2 is not specified.
+- Add some defines to configure.ac to enable exporting of values and packaging,
+and clean up output.
+- Give convenient summary at end of ./configure.
+- Display version information and add --version command line option, and make
+sure we flush stdout.
+- Enable curses after the mining threads are set up so that failure messages
+won't be lost in the curses interface.
+- Disable curses after inputting a pool if we requested no curses interface.
+- Add an option to break out after successfully mining a number of accepted
+shares.
+- Exit with a failed return code if we did not reach opt_shares.
+- The cpu mining work data can get modified before we copy it if we submit it
+async, and the sync submission is not truly sync anyway, so just submit it sync.
+
+
+Version 1.6.0 - August 26, 2011
+
+- Make restarting of GPUs optional for systems that hang on any attempt to
+restart them.     Fix DEAD status by comparing it to last live time rather than
+last attempted restart time since that happens every minute.
+- Move staged threads to hashes so we can sort them by time.
+- Create a hash list of all the blocks created and search them to detect when a
+new block has definitely appeared, using that information to detect stale work
+and discard it.
+- Update configure.ac for newer autoconf tools.
+- Use the new hashes directly for counts instead of the fragile counters
+currently in use.
+- Update to latest sse2 code from cpuminer-ng.
+- Allow LP to reset block detect and block detect lp flags to know who really
+came first.
+- Get start times just before mining begins to not have very slow rise in
+average.
+- Add message about needing one server.
+- We can queue all the necessary work without hitting frequent stales now with
+the time and string stale protection active all the time.     This prevents a
+pool being falsely labelled as not providing work fast enough.
+- Include uthash.h in distro.
+- Implement SSE2 32 bit assembly algorithm as well.
+- Fail gracefully if unable to open the opencl files.
+- Make cgminer look in the install directory for the .cl files making make
+install work correctly.
+- Allow a custom kernel path to be entered on the command line.
+- Bump threshhold for lag up to maximum queued but no staged work.
+- Remove fragile source patching for bitalign, vectors et. al and simply pass it
+with the compiler options.
+- Actually check the value returned for the x-roll-ntime extension to make sure
+it isn't saying N.
+- Prevent segfault on exit for when accessory threads don't exist.
+- Disable curl debugging with opt protocol since it spews to stderr.
+
+
+Version 1.5.8 - August 23, 2011
+
+- Minimise how much more work can be given in cpu mining threads each interval.
+- Make the fail-pause progressively longer each time it fails until the network
+recovers.
+- Only display the lagging message if we've requested the work earlier.
+- Clean up the pool switching to not be dependent on whether the work can roll
+or not by setting a lagging flag and then the idle flag.
+- Only use one thread to determine if a GPU is sick or well, and make sure to
+reset the sick restart attempt time.
+- The worksize was unintentionally changed back to 4k by mistake, this caused a
+slowdown.
+
+
+Version 1.5.7 - August 22, 2011
+
+- Fix a crash with --algo auto
+- Test at appropriate target difficulty now.
+- Add per-device statics log output with --per-device-stats
+- Fix breakage that occurs when 1 or 4 vectors are chosen on new phatk.
+- Make rolltime report debug level only now since we check it every work
+item.
+- Add the ability to enable/disable per-device stats on the fly and match
+logging on/off.
+- Explicitly tell the compiler to retain the program to minimise the chance of
+the zero sized binary errors.
+- Add one more instruction to avoid one branch point in the common path in the
+cl return code. Although this adds more ALUs overall and more branch points, the
+common path code has the same number of ALUs and one less jmp, jmps being more
+expensive.
+- Explicitly link in ws2_32 on the windows build and update README file on how
+to compile successfully on windows.
+- Release cl resources should the gpu mining thread abort.
+- Attempt to restart a GPU once every minute while it's sick.
+- Don't kill off the reinit thread if it fails to init a GPU but returns safely.
+- Only declare a GPU dead if there's been no sign of activity from the reinit
+thread for 10 mins.
+- Never automatically disable any pools but just specify them as idle if they're
+unresponsive at startup.
+- Use any longpoll available, and don't disable it if switching to a server that
+doesn't have it. This allows you to mine solo, yet use the longpoll from a pool
+even if the pool is the backup server.
+- Display which longpoll failed and don't free the ram for lp_url since it
+belongs to the pool hdr path.
+- Make the tcp setsockopts unique to linux in the hope it allows freebsd et. al
+to compile.
+
+
+Version 1.5.6 - August 17, 2011
+
+- New phatk and poclbm kernels. Updated phatk to be in sync with latest 2.2
+courtesy of phateus. Custom modified to work best with cgminer.
+- Updated output buffer code to use a smaller buffer with the kernels.
+- Clean up the longpoll management to ensure the right paths go to the right
+pool and display whether we're connected to LP or not in the status line.
+
+
+Version 1.5.5 - August 16, 2011
+
+- Rework entirely the GPU restart code. Strike a balance between code that
+re-initialises the GPU entirely so that soft hangs in the code are properly
+managed, but if a GPU is completely hung, the thread restart code fails
+gracefully, so that it does not take out any other code or devices. This will
+allow cgminer to keep restarting GPUs that can be restarted, but continue
+mining even if one or more GPUs hangs which would normally require a reboot.
+- Add --submit-stale option which submits all shares, regardless of whether they
+would normally be considered stale.
+- Keep options in alphabetical order.
+- Probe for slightly longer for when network conditions are lagging.
+- Only display the CPU algo when we're CPU mining.
+- As we have keepalives now, blaming network flakiness on timeouts appears to
+have been wrong.     Set a timeout for longpoll to 1 hour, and most other
+network connectivity to 1 minute.
+- Simplify output code and remove HW errors from CPU stats.
+- Simplify code and tidy output.
+- Only show cpu algo in summary if cpu mining.
+- Log summary at the end as per any other output.
+- Flush output.
+- Add a linux-usb-cgminer guide courtesy of Kano.
+
+
+Version 1.5.4 - August 14, 2011
+
+- Add new option: --monitor <cmd> Option lets user specify a command <cmd> that
+will get forked by cgminer on startup. cgminer's stderr output subsequently gets
+piped directly to this command.
+- Allocate work from one function to be able to initialise variables added
+later.
+- Add missing fflush(stdout) for --ndevs and conclusion summary.
+- Preinitialise the devices only once on startup.
+- Move the non cl_ variables into the cgpu info struct to allow creating a new
+cl state on reinit, preserving known GPU variables.
+- Create a new context from scratch in initCQ in case something was corrupted to
+maximise our chance of succesfully creating a new worker thread. Hopefully this
+makes thread restart on GPU failure more reliable, without hanging everything
+in the case of a completely wedged GPU.
+- Display last initialised time in gpu management info, to know if a GPU has
+been re-initialised.
+- When pinging a sick cpu, flush finish and then ping it in a separate thread in
+the hope it recovers without needing a restart, but without blocking code
+elsewhere.
+- Only consider a pool lagging if we actually need the work and we have none
+staged despite queue requests stacking up. This decreases significantly the
+amount of work that leaks to the backup pools.
+- The can_roll function fails inappropriately in stale_work.
+- Only put the message that a pool is down if not pinging it every minute. This
+prevents cgminer from saying pool down at 1 minute intervals unless in debug
+mode.
+- Free all work in one place allowing us to perform actions on it in the future.
+- Remove the extra shift in the output code which was of dubious benefit. In
+fact in cgminer's implementation, removing this caused a miniscule speedup.
+- Test each work item to see if it can be rolled instead of per-pool and roll
+whenever possible, adhering to the 60 second timeout. This makes the period
+after a longpoll have smaller dips in throughput, as well as requiring less
+getworks overall thus increasing efficiency.
+- Stick to rolling only work from the current pool unless we're in load balance
+mode or lagging to avoid aggressive rolling imitating load balancing.
+- If a work item has had any mining done on it, don't consider it discarded
+work.
+
+
+Version 1.5.3 - July 30, 2011
+
+- Significant work went into attempting to make the thread restart code robust
+to identify sick threads, tag them SICK after 1 minute, then DEAD after 5
+minutes of inactivity and try to restart them. Instead of re-initialising the
+GPU completely, only a new cl context is created to avoid hanging the rest of
+the GPUs should the dead GPU be hung irrevocably.
+- Use correct application name in syslog.
+- Get rid of extra line feeds.
+- Use pkg-config to check for libcurl version
+- Implement per-thread getwork count with proper accounting to not over-account
+queued items when local work replaces it.
+- Create a command queue from the program created from source which allows us
+to flush the command queue in the hope it will not generate a zero sized binary
+any more.
+- Be more willing to get work from the backup pools if the work is simply being
+queued faster than it is being retrieved.
+
+
+Version 1.5.2 - July 28, 2011
+
+- Restarting a hung GPU can hang the rest of the GPUs so just declare it dead
+and provide the information in the status.
+- The work length in the miner thread gets smaller but doesn't get bigger if
+it's under 1 second.     This could end up leading to CPU under-utilisation and
+lower and lower hash rates.     Fix it by increasing work length if it drops
+under 1 second.
+- Make the "quiet" mode still update the status and display errors, and add a
+new --real-quiet option which disables all output and can be set once while
+running.
+- Update utility and efficiency figures when displaying them.
+- Some Intel HD graphics support the opencl commands but return errors since
+they don't support opencl. Don't fail with them, just provide a warning and
+disable GPU mining.
+- Add http:// if it's not explicitly set for URL entries.
+- Log to the output file at any time with warnings and errors, instead of just
+when verbose mode is on.
+- Display the correct current hash as per blockexplorer, truncated to 16
+characters, with just the time.
+
+
+Version 1.5.1 - July 27, 2011
+
+- Two redraws in a row cause a crash in old libncurses so just do one redraw
+using the main window.
+- Don't adjust hash_div only up for GPUs. Disable hash_div adjustment for GPUs.
+- Only free the thread structures if the thread still exists.
+- Update both windows separately, but not at the same time to prevent the double
+refresh crash that old libncurses has.     Do the window resize check only when
+about to redraw the log window to minimise ncurses cpu usage.
+- Abstract out the decay time function and use it to make hash_div a rolling
+average so it doesn't change too abruptly and divide work in chunks large enough
+to guarantee they won't overlap.
+- Sanity check to prove locking.
+- Don't take more than one lock at a time.
+- Make threads report out when they're queueing a request and report if they've
+failed.
+- Make cpu mining work submission asynchronous as well.
+- Properly detect stale work based on time from staging and discard instead of
+handing on, but be more lax about how long work can be divided for up to the
+scantime.
+- Do away with queueing work separately at the start and let each thread grab
+its own work as soon as it's ready.
+- Don't put an extra work item in the queue as each new device thread will do so
+itself.
+- Make sure to decrease queued count if we discard the work.
+- Attribute split work as local work generation.
+- If work has been cloned it is already at the head of the list and when being
+reinserted into the queue it should be placed back at the head of the list.
+- Dividing work is like the work is never removed at all so treat it as such.
+However the queued bool needs to be reset to ensure we *can* request more work
+even if we didn't initially.
+- Make the display options clearer.
+- Add debugging output to tq_push calls.
+- Add debugging output to all tq_pop calls.
+
+
+Version 1.5.0 - July 26, 2011
+
+- Increase efficiency of slow mining threads such as CPU miners dramatically. Do
+this by detecting which threads cannot complete searching a work item within the
+scantime and then divide up a work item into multiple smaller work items.
+Detect the age of the work items and if they've been cloned before to prevent
+doing the same work over. If the work is too old to be divided, then see if it
+can be time rolled and do that to generate work. This dramatically decreases the
+number of queued work items from a pool leading to higher overall efficiency
+(but the same hashrate and share submission rate).
+- Don't request work too early for CPUs as CPUs will scan for the full
+opt_scantime anyway.
+- Simplify gpu management enable/disable/restart code.
+- Implement much more accurate rolling statistics per thread and per gpu and
+improve accuracy of rolling displayed values.
+- Make the rolling log-second average more accurate.
+- Add a menu to manage GPUs on the fly allowing you to enable/disable GPUs or
+try restarting them.
+- Keep track of which GPUs are alive versus enabled.
+- Start threads for devices that are even disabled, but don't allow them to
+start working.
+- The last pool is when we are low in total_pools, not active_pools.
+- Make the thread restart do a pthread_join after disabling the device, only
+re-enabling it if we succeed in restarting the thread. Do this from a separate
+thread so as to not block any other code.This will allow cgminer to continue
+even if one GPU hangs.
+- Try to do every curses manipulation under the curses lock.
+- Only use the sockoptfunction if the version of curl is recent enough.
+
+
+Version 1.4.1 - July 24, 2011
+
+- Do away with GET for dealing with longpoll forever. POST is the one that works
+everywhere, not the other way around.
+- Detect when the primary pool is lagging and start queueing requests on backup
+pools if possible before needing to roll work.
+- Load balancing puts more into the current pool if there are disabled pools.
+Fix.
+- Disable a GPU device should the thread fail to init.
+- Out of order command queue may fail on osx. Try without if it fails.
+- Fix possible dereference on blank inputs during input_pool.
+- Defines missing would segfault on --help when no sse mining is built in.
+- Revert "Free up resources/stale compilers." - didn't help.
+- Only try to print the status of active devices or it would crash.
+- Some hardware might benefit from the less OPS so there's no harm in leaving
+kernel changes that do that apart from readability of the code.
+
+Version 1.4.0 - July 23, 2011
+
+- Feature upgrade: Add keyboard input during runtime to allow modification of
+and viewing of numerous settings such as adding/removing pools, changing
+multipool management strategy, switching pools, changing intensiy, verbosity,
+etc. with a simple keypress menu system.
+- Free up resources/stale compilers.
+- Kernels are safely flushed in a way that allows out of order execution to
+work.
+- Sometimes the cl compiler generates zero sized binaries and only a reboot
+seems to fix it.
+- Don't try to stop/cancel threads that don't exist.
+- Only set option to show devices and exit if built with opencl support.
+- Enable curses earlier and exit with message in main for messages to not be
+lost in curses windows.
+- Make it possible to enter server credentials with curses input if none are
+specified on the command line.
+- Abstract out a curses input function and separate input pool function to allow
+for live adding of pools later.
+- Remove the nil arguments check to allow starting without parameters.
+- Disable/enable echo & cbreak modes.
+- Add a thread that takes keyboard input and allow for quit, silent, debug,
+verbose, normal, rpc protocol debugging and clear screen options.
+- Add pool option to input and display current pool status, pending code to
+allow live changes.
+- Add a bool for explicit enabling/disabling of pools.
+- Make input pool capable of bringing up pools while running.
+- Do one last check of the work before submitting it.
+- Implement the ability to live add, enable, disable, and switch to pools.
+- Only internally test for block changes when the work matches the current pool
+to prevent interleaved block change timing on multipools.
+- Display current pool management strategy to enable changing it on the fly.
+- The longpoll blanking of the current_block data may not be happening before
+the work is converted and appears to be a detected block change.     Blank the
+current block be
+- Make --no-longpoll work again.
+- Abstract out active pools count.
+- Allow the pool strategy to be modified on the fly.
+- Display pool information on the fly as well.
+- Add a menu and separate out display options.
+- Clean up the messy way the staging thread communicates with the longpoll
+thread to determine who found the block first.
+- Make the input windows update immediately instead of needing a refresh.
+- Allow log interval to be set in the menu.
+- Allow scan settings to be modified at runtime.
+- Abstract out the longpoll start and explicitly restart it on pool change.
+- Make it possible to enable/disable longpoll.
+- Set priority correctly on multipools.     Display priority and alive/dead
+information in display_pools.
+- Implement pool removal.
+- Limit rolltime work generation to 10 iterations only.
+- Decrease testing log to info level.
+- Extra refresh not required.
+- With huge variation in GPU performance, allow intensity to go from -10 to +10.
+- Tell getwork how much of a work item we're likely to complete for future
+splitting up of work.
+- Remove the mandatory work requirement at startup by testing for invalid work
+being passed which allows for work to be queued immediately.     This also
+removes the requirem
+- Make sure intensity is carried over to thread count and is at least the
+minimum necessary to work.
+- Unlocking error on retry. Locking unnecessary anyway so remove it.
+- Clear log window from consistent place. No need for locking since logging is
+disabled during input.
+- Cannot print the status of threads that don't exist so just queue enough work
+for the number of mining threads to prevent crash with -Q N.
+- Update phatk kernel to one with new parameters for slightly less overhead
+again.     Make the queue kernel parameters call a function pointer to select
+phatk or poclbm.
+- Make it possible to select the choice of kernel on the command line.
+- Simplify the output part of the kernel. There's no demonstrable advantage from
+more complexity.
+- Merge pull request #18 from ycros/cgminer
+- No need to make leaveok changes win32 only.
+- Build support in for all SSE if possible and only set the default according to
+machine capabilities.
+- Win32 threading and longpoll keepalive fixes.
+- Win32: Fix for mangled output on the terminal on exit.
+
+
+Version 1.3.1 - July 20, 2011
+
+- Feature upgrade; Multiple strategies for failover. Choose from default which
+now falls back to a priority order from 1st to last, round robin which only
+changes pools when one is idle, rotate which changes pools at user-defined
+intervals, and load-balance which spreads the work evenly amongst all pools.
+- Implement pool rotation strategy.
+- Implement load balancing algorithm by rotating requests to each pool.
+- Timeout on failed discarding of staged requests.
+- Implement proper flagging of idle pools, test them with the watchdog thread,
+and failover correctly.
+- Move pool active test to own function.
+- Allow multiple strategies to be set for multipool management.
+- Track pool number.
+- Don't waste the work items queued on testing the pools at startup.
+- Reinstate the mining thread watchdog restart.
+- Add a getpoll bool into the thread information and don't restart threads stuck
+waiting on work.
+- Rename the idlenet bool for the pool for later use.
+- Allow the user/pass userpass urls to be input in any order.
+- When json rpc errors occur they occur in spits and starts, so trying to limit
+them with the comms error bool doesn't stop a flood of them appearing.
+- Reset the queued count to allow more work to be queued for the new pool on
+pool switch.
+
+Version 1.3.0 - July 19, 2011
+
+- Massive infrastructure update to support pool failover.
+- Accept multiple parameters for url, user and pass and set up structures of
+pool data accordingly.
+- Probe each pool for what it supports.
+- Implement per pool feature support according to rolltime support as
+advertised by server.
+- Do switching automatically based on a 300 second timeout of locally generated
+work or 60 seconds of no response from a server that doesn't support rolltime.
+- Implement longpoll server switching.
+- Keep per-pool data and display accordingly.
+- Make sure cgminer knows how long the pool has actually been out for before
+deeming it a prolonged outage.
+- Fix bug with ever increasing staged work in 1.2.8 that eventually caused
+infinite rejects.
+- Make warning about empty http requests not show by default since many
+servers do this regularly.
+
+
+Version 1.2.8 - July 18, 2011
+
+- More OSX build fixes.
+- Add an sse4 algorithm to CPU mining.
+- Fix CPU mining with other algorithms not working.
+- Rename the poclbm file to ensure a new binary is built since.
+- We now are guaranteed to have one fresh work item after a block change and we
+should only discard staged requests.
+- Don't waste the work we retrieve from a longpoll.
+- Provide a control lock around global bools to avoid racing on them.
+- Iterating over 1026 nonces when confirming data from the GPU is old code
+and unnecessary and can lead to repeats/stales.
+- The poclbm kernel needs to be updated to work with the change to 4k sized
+output buffers.
+- longpoll seems to work either way with post or get but some servers prefer
+get so change to httpget.
+
+
+Version 1.2.7 - July 16, 2011
+
+- Show last 8 characters of share submitted in log.
+- Display URL connected to and user logged in as in status.
+- Display current block and when it was started in the status line.
+- Only pthread_join the mining threads if they exist as determined by
+pthread_cancel and don't fail on pthread_cancel.
+- Create a unique work queue for all getworks instead of binding it to thread 0
+to avoid any conflict over thread 0's queue.
+- Clean up the code to make it clear it's watchdog thread being messaged to
+restart the threads.
+- Check the current block description hasn't been blanked pending the real
+new current block data.
+- Re-enable signal handlers once the signal has been received to make it
+possible to kill cgminer if it fails to shut down.
+- Disable restarting of CPU mining threads pending further investigation.
+- Update longpoll messages.
+- Add new block data to status line.
+- Fix opencl tests for osx.
+- Only do local generation of work if the work item is not stale itself.
+- Check for stale work within the mining threads and grab new work if
+positive.
+- Test for idle network conditions and prevent threads from being restarted
+by the watchdog thread under those circumstances.
+- Make sure that local work generation does not continue indefinitely by
+stopping it after 10 minutes.
+- Tweak the kernel to have a shorter path using a 4k buffer and a mask on the
+nonce value instead of a compare and loop for a shorter code path.
+- Allow queue of zero and make that default again now that we can track how
+work is being queued versus staged. This can decrease reject rates.
+- Queue precisely the number of mining threads as longpoll_staged after a
+new block to not generate local work.
+
+
+Version 1.2.6 - July 15, 2011
+
+- Put a current system status line beneath the total work status line
+- Fix a counting error that would prevent cgminer from correctly detecting
+situations where getwork was failing - this would cause stalls sometimes
+unrecoverably.
+- Limit the maximum number of requests that can be put into the queue which
+otherwise could get arbitrarily long during a network outage.
+- Only count getworks that are real queue requests.
+
+
+Version 1.2.5 - July 15, 2011
+
+- Conflicting -n options corrected
+- Setting an intensity with -I disables dynamic intensity setting
+- Removed option to manually disable dynamic intensity
+- Improve display output
+- Implement signal handler and attempt to clean up properly on exit
+- Only restart threads that are not stuck waiting on mandatory getworks
+- Compatibility changes courtesy of Ycros to build on mingw32 and osx
+- Explicitly grab first work item to prevent false positive hardware errors
+due to working on uninitialised work structs
+- Add option for non curses --text-only output
+- Ensure we connect at least once successfully before continuing to retry to
+connect in case url/login parameters were wrong
+- Print an executive summary when cgminer is terminated
+- Make sure to refresh the status window
+
+Versions -> 1.2.4
+
+- Con Kolivas - July 2011. New maintainership of code under cgminer name.
+- Massive rewrite to incorporate GPU mining.
+- Incorporate original oclminer c code.
+- Rewrite gpu mining code to efficient work loops.
+- Implement per-card detection and settings.
+- Implement vector code.
+- Implement bfi int patching.
+- Import poclbm and phatk ocl kernels and use according to hardware type.
+- Implement customised optimised versions of opencl kernels.
+- Implement binary kernel generation and loading.
+- Implement preemptive asynchronous threaded work gathering and pushing.
+- Implement variable length extra work queues.
+- Optimise workloads to be efficient miners instead of getting lots of extra
+  work.
+- Implement total hash throughput counters, per-card accepted, rejected and
+  hw error count.
+- Staging and watchdog threads to prevent fallover.
+- Stale and reject share guarding.
+- Autodetection of new blocks without longpoll.
+- Dynamic setting of intensity to maintain desktop interactivity.
+- Curses interface with generous statistics and information.
+- Local generation of work (xroll ntime) when detecting poor network
+connectivity.
+
+Version 1.0.2
+
+- Linux x86_64 optimisations - Con Kolivas
+- Optimise for x86_64 by default by using sse2_64 algo
+- Detects CPUs and sets number of threads accordingly
+- Uses CPU affinity for each thread where appropriate
+- Sets scheduling policy to lowest possible
+- Minor performance tweaks
+
+Version 1.0.1 - May 14, 2011
+
+- OSX support
+
+Version 1.0 - May 9, 2011
+
+- jansson 2.0 compatibility
+- correct off-by-one in date (month) display output
+- fix platform detection
+- improve yasm configure bits
+- support full URL, in X-Long-Polling header
+
+Version 0.8.1 - March 22, 2011
+
+- Make --user, --pass actually work
+
+- Add User-Agent HTTP header to requests, so that server operators may
+  more easily identify the miner client.
+
+- Fix minor bug in example JSON config file
+
+Version 0.8 - March 21, 2011
+
+- Support long polling: http://deepbit.net/longpolling.php
+
+- Adjust max workload based on scantime (default 5 seconds,
+  or 60 seconds for longpoll)
+
+- Standardize program output, and support syslog on Unix platforms
+
+- Suport --user/--pass options (and "user" and "pass" in config file),
+  as an alternative to the current --userpass
+
+Version 0.7.2 - March 14, 2011
+
+- Add port of ufasoft's sse2 assembly implementation (Linux only)
+  This is a substantial speed improvement on Intel CPUs.
+
+- Move all JSON-RPC I/O to separate thread.  This reduces the
+  number of HTTP connections from one-per-thread to one, reducing resource
+  usage on upstream bitcoind / pool server.
+
+Version 0.7.1 - March 2, 2011
+
+- Add support for JSON-format configuration file.  See example
+  file example-cfg.json.  Any long argument on the command line
+  may be stored in the config file.
+- Timestamp each solution found
+- Improve sha256_4way performance.  NOTE: This optimization makes
+  the 'hash' debug-print output for sha256_way incorrect.
+- Use __builtin_expect() intrinsic as compiler micro-optimization
+- Build on Intel compiler
+- HTTP library now follows HTTP redirects
+
+Version 0.7 - February 12, 2011
+
+- Re-use CURL object, thereby reuseing DNS cache and HTTP connections
+- Use bswap_32, if compiler intrinsic is not available
+- Disable full target validation (as opposed to simply H==0) for now
+
+Version 0.6.1 - February 4, 2011
+
+- Fully validate "hash < target", rather than simply stopping our scan
+  if the high 32 bits are 00000000.
+- Add --retry-pause, to set length of pause time between failure retries
+- Display proof-of-work hash and target, if -D (debug mode) enabled
+- Fix max-nonce auto-adjustment to actually work.  This means if your
+  scan takes longer than 5 seconds (--scantime), the miner will slowly
+  reduce the number of hashes you work on, before fetching a new work unit.
+
+Version 0.6 - January 29, 2011
+
+- Fetch new work unit, if scanhash takes longer than 5 seconds (--scantime)
+- BeeCee1's sha256 4way optimizations
+- lfm's byte swap optimization (improves via, cryptopp)
+- Fix non-working short options -q, -r
+
+Version 0.5 - December 28, 2010
+
+- Exit program, when all threads have exited
+- Improve JSON-RPC failure diagnostics and resilience
+- Add --quiet option, to disable hashmeter output.
+
+Version 0.3.3 - December 27, 2010
+
+- Critical fix for sha256_cryptopp 'cryptopp_asm' algo
+
+Version 0.3.2 - December 23, 2010
+
+- Critical fix for sha256_via
+
+Version 0.3.1 - December 19, 2010
+
+- Critical fix for sha256_via
+- Retry JSON-RPC failures (see --retry, under "minerd --help" output)
+
+Version 0.3 - December 18, 2010
+
+- Add crypto++ 32bit assembly implementation
+- show version upon 'minerd --help'
+- work around gcc 4.5.x bug that killed 4way performance
+
+Version 0.2.2 - December 6, 2010
+
+- VIA padlock implementation works now
+- Minor build and runtime fixes
+
+Version 0.2.1 - November 29, 2010
+
+- avoid buffer overflow when submitting solutions
+- add Crypto++ sha256 implementation (C only, ASM elided for now)
+- minor internal optimizations and cleanups
+
+Version 0.2 - November 27, 2010
+
+- Add script for building a Windows installer
+- improve hash performance (hashmeter) statistics
+- add tcatm 4way sha256 implementation
+- Add experimental VIA Padlock sha256 implementation
+
+Version 0.1.2 - November 26, 2010
+
+- many small cleanups and micro-optimizations
+- build win32 exe using mingw
+- RPC URL, username/password become command line arguments
+- remove unused OpenSSL dependency
+
+Version 0.1.1 - November 24, 2010
+
+- Do not build sha256_generic module separately from cpuminer.
+
+Version 0.1 - November 24, 2010
+
+- Initial release.
+

+ 19 - 0
README.gekko.txt

@@ -0,0 +1,19 @@
+##################################################
+#author: Novak                                   #
+#email: novak@gekkoscience.com                   #
+#BTC address: 1NovakVK1FWdh9gs41dckVQS8bxSzwiaRw #
+##################################################
+
+You may compile compac support with the ./configure option --enable-gekko.
+You can set the clock speed between 100 and 500 MHz using the --compac-freq option, do not expect it to run over about 150MHz on stock USB power (depending on voltage and the individual stick).
+
+There is an example file showing usage: cgminer_run.sh
+Usage:
+./cgminer --compac-freq <num>
+
+<num> is any integer from 100 to 500.
+
+You can calculate the expected hashrate as 0.055*(clock_in_mhz)=GH/s.  For example, 200 MHz gives 11GH/s.
+
+--
+novak

+ 90 - 0
README.md

@@ -0,0 +1,90 @@
+### #########################################################################
+##  CGminer 4.12.1 GekkoScience Compac, 2pac & Newpack & CompacF & terminus #
+### #########################################################################
+
+This is cgminer 4.12.1 with support for GekkoScience Compac, CompacF, 2pac.
+
+This software is forked from cgminer 4.10.0 original from ckolivas.
+
+Then i added v.thoang gekko drivers and kanoi upgrade, avalon updates , xtranonce
+
+(you can refer to original documentation to docs/README)
+
+### GekkoScience compac & 2pac Usb miner ##
+<p align="center">
+<img src="https://raw.githubusercontent.com/wareck/cgminer-gekko/master/docs/gekko.jpg">
+<img src="https://raw.githubusercontent.com/wareck/cgminer-gekko/master/docs/2pac.jpg">
+</p>
+
+### GekkoScience Newpac & CompacF Usb miner ##
+<p align="center">
+<img src="https://raw.githubusercontent.com/wareck/cgminer-gekko/master/docs/newpac.jpg">
+<img src="https://raw.githubusercontent.com/wareck/cgminer-gekko/master/docs/compacf.png">
+</p>
+
+### GekkoScience Terminus r606 / R909 ##
+<p align="center">
+<img src="https://raw.githubusercontent.com/wareck/cgminer-gekko/master/docs/terminus.jpg">
+</p>
+
+This software use a slighty moddified driver from novak's gekko driver.
+
+I allows working with icarus miner and gekko on same rig.
+
+to build this specific code on linux:
+
+	sudo apt-get update -y
+	sudo apt-get install build-essential autoconf automake libtool pkg-config libcurl4-openssl-dev libudev-dev \
+	libjansson-dev libncurses5-dev libusb-1.0-0-dev zlib1g-dev git -y
+
+	git clone https://github.com/wareck/cgminer-gekko.git
+
+	cd cgminer-gekko
+	sudo usermod -a -G dialout,plugdev $USER
+	sudo cp 01-cgminer.rules /etc/udev/rules.d/
+	CFLAGS="-O2 -march=native" ./autogen.sh
+	./configure --enable-gekko
+	make
+	sudo make install
+	sudo reboot
+	
+### Option Summary ###
+
+```
+  --gekko-compac-freq <clock>   Chip clock speed (MHz) default is 200 Mhz
+  --gekko-2pac-freq <clock> Chip clock speed (Mhz) default is 150 Mhz 
+  --gekko-newpac-freq <clock> Chip clock speed (Mhz) default is 150 Mhz
+  --gekko-r606-freq <clock> Set GekkoScience Terminus R606 frequency in MHz, range 50-900 (default: 550)
+  --gekko-r909-freq <clock> Set GekkoScience Terminus R606 frequency in MHz, range 50-900 (default: 550
+  --gekko-terminus-detect Detect GekkoScience Terminus BM1384
+  --suggest-diff <value> Limit diff for starting mine default is 32
+```
+
+### Command line ###
+
+```
+ ./cgminer -o pool_url -u username -p password --gekko-compac-freq 200 --gekko-2pac-freq 150 --gekko-newpac-freq 150
+```
+
+For windows users, you can donwload the release zip file
+
+Inside you can find a cgminer_run.bat file and you can adjust you settings.
+
+### Nicehash extranonce support ##
+
+You can use your miner with last extranonce support for nicehash by adding #xnsub at the address end, like this:
+
+	./cgminer -o stratum+tcp://sha256.eu.nicehash.com:3334#xnsub -u my_btc_address -p x --gekko-compac-freq 200 --gekko-2pac-freq 150 --gekko-newpac-freq 150
+
+### For Raspberry Users : ###
+
+You may need to use "legacy" version of raspberry OS (32 or 64 bit no matter).
+
+## Credits
+```
+Kanoi    : https://github.com/kanoi/cgminer.git (code update to 4.12.0 and gekko driver improvement)
+ckolivas : https://github.com/ckolivas/cgminer.git (original cgminer code)
+vthoang  : https://github.com/vthoang/cgminer.git (gekko driver)
+nicehash : https://github.com/nicehash/cgminer-ckolivas.git (nicheash extranonce)
+prmam : coinbase flags fix and default configuration, udev permissions fix
+chipjarred : macos compilation fix, memory leak fix, and Fixing change of sign warnings when referring to buffers

+ 334 - 0
api-example.c

@@ -0,0 +1,334 @@
+/*
+ * Copyright 2011 Kano
+ *
+ * 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.
+ */
+
+/* Compile:
+ *   gcc api-example.c -o cgminer-api
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "compat.h"
+#include "util.h"
+
+#if defined(unix) || defined(__APPLE__)
+	#include <errno.h>
+	#include <sys/socket.h>
+	#include <netinet/in.h>
+	#include <arpa/inet.h>
+	#include <netdb.h>
+
+	#define SOCKETFAIL(a) ((a) < 0)
+	#define INVSOCK -1
+	#define CLOSESOCKET close
+
+	#define SOCKETINIT {}
+
+	#define SOCKERRMSG strerror(errno)
+#endif
+
+#ifdef WIN32
+	#include <winsock2.h>
+
+	#define SOCKETTYPE SOCKET
+	#define SOCKETFAIL(a) ((a) == SOCKET_ERROR)
+	#define INVSOCK INVALID_SOCKET
+	#define CLOSESOCKET closesocket
+
+	static char WSAbuf[1024];
+
+	struct WSAERRORS {
+		int id;
+		char *code;
+	} WSAErrors[] = {
+		{ 0,			"No error" },
+		{ WSAEINTR,		"Interrupted system call" },
+		{ WSAEBADF,		"Bad file number" },
+		{ WSAEACCES,		"Permission denied" },
+		{ WSAEFAULT,		"Bad address" },
+		{ WSAEINVAL,		"Invalid argument" },
+		{ WSAEMFILE,		"Too many open sockets" },
+		{ WSAEWOULDBLOCK,	"Operation would block" },
+		{ WSAEINPROGRESS,	"Operation now in progress" },
+		{ WSAEALREADY,		"Operation already in progress" },
+		{ WSAENOTSOCK,		"Socket operation on non-socket" },
+		{ WSAEDESTADDRREQ,	"Destination address required" },
+		{ WSAEMSGSIZE,		"Message too long" },
+		{ WSAEPROTOTYPE,	"Protocol wrong type for socket" },
+		{ WSAENOPROTOOPT,	"Bad protocol option" },
+		{ WSAEPROTONOSUPPORT,	"Protocol not supported" },
+		{ WSAESOCKTNOSUPPORT,	"Socket type not supported" },
+		{ WSAEOPNOTSUPP,	"Operation not supported on socket" },
+		{ WSAEPFNOSUPPORT,	"Protocol family not supported" },
+		{ WSAEAFNOSUPPORT,	"Address family not supported" },
+		{ WSAEADDRINUSE,	"Address already in use" },
+		{ WSAEADDRNOTAVAIL,	"Can't assign requested address" },
+		{ WSAENETDOWN,		"Network is down" },
+		{ WSAENETUNREACH,	"Network is unreachable" },
+		{ WSAENETRESET,		"Net connection reset" },
+		{ WSAECONNABORTED,	"Software caused connection abort" },
+		{ WSAECONNRESET,	"Connection reset by peer" },
+		{ WSAENOBUFS,		"No buffer space available" },
+		{ WSAEISCONN,		"Socket is already connected" },
+		{ WSAENOTCONN,		"Socket is not connected" },
+		{ WSAESHUTDOWN,		"Can't send after socket shutdown" },
+		{ WSAETOOMANYREFS,	"Too many references, can't splice" },
+		{ WSAETIMEDOUT,		"Connection timed out" },
+		{ WSAECONNREFUSED,	"Connection refused" },
+		{ WSAELOOP,		"Too many levels of symbolic links" },
+		{ WSAENAMETOOLONG,	"File name too long" },
+		{ WSAEHOSTDOWN,		"Host is down" },
+		{ WSAEHOSTUNREACH,	"No route to host" },
+		{ WSAENOTEMPTY,		"Directory not empty" },
+		{ WSAEPROCLIM,		"Too many processes" },
+		{ WSAEUSERS,		"Too many users" },
+		{ WSAEDQUOT,		"Disc quota exceeded" },
+		{ WSAESTALE,		"Stale NFS file handle" },
+		{ WSAEREMOTE,		"Too many levels of remote in path" },
+		{ WSASYSNOTREADY,	"Network system is unavailable" },
+		{ WSAVERNOTSUPPORTED,	"Winsock version out of range" },
+		{ WSANOTINITIALISED,	"WSAStartup not yet called" },
+		{ WSAEDISCON,		"Graceful shutdown in progress" },
+		{ WSAHOST_NOT_FOUND,	"Host not found" },
+		{ WSANO_DATA,		"No host data of that type was found" },
+		{ -1,			"Unknown error code" }
+	};
+
+	static char *WSAErrorMsg()
+	{
+		char *msg;
+		int i;
+		int id = WSAGetLastError();
+
+		/* Assume none of them are actually -1 */
+		for (i = 0; WSAErrors[i].id != -1; i++)
+			if (WSAErrors[i].id == id)
+				break;
+
+		sprintf(WSAbuf, "Socket Error: (%d) %s", id, WSAErrors[i].code);
+
+		return &(WSAbuf[0]);
+	}
+
+	#define SOCKERRMSG WSAErrorMsg()
+
+	static WSADATA WSA_Data;
+
+	#define SOCKETINIT	int wsa; \
+				if (wsa = WSAStartup(0x0202, &WSA_Data)) { \
+					printf("Socket startup failed: %d\n", wsa); \
+					return 1; \
+				}
+
+	#ifndef SHUT_RDWR
+	#define SHUT_RDWR SD_BOTH
+	#endif
+#endif
+
+static const char SEPARATOR = '|';
+static const char COMMA = ',';
+static const char EQ = '=';
+static int ONLY;
+
+void display(char *buf)
+{
+	char *nextobj, *item, *nextitem, *eq;
+	int itemcount;
+
+	while (buf != NULL) {
+		nextobj = strchr(buf, SEPARATOR);
+		if (nextobj != NULL)
+			*(nextobj++) = '\0';
+
+		if (*buf) {
+			item = buf;
+			itemcount = 0;
+			while (item != NULL) {
+				nextitem = strchr(item, COMMA);
+				if (nextitem != NULL)
+					*(nextitem++) = '\0';
+
+				if (*item) {
+					eq = strchr(item, EQ);
+					if (eq != NULL)
+						*(eq++) = '\0';
+
+					if (itemcount == 0)
+						printf("[%s%s] =>\n(\n", item, (eq != NULL && isdigit(*eq)) ? eq : "");
+
+					if (eq != NULL)
+						printf("   [%s] => %s\n", item, eq);
+					else
+						printf("   [%d] => %s\n", itemcount, item);
+				}
+
+				item = nextitem;
+				itemcount++;
+			}
+			if (itemcount > 0)
+				puts(")");
+		}
+
+		buf = nextobj;
+	}
+}
+
+#define SOCKSIZ 65535
+
+int callapi(char *command, char *host, short int port)
+{
+	struct hostent *ip;
+	struct sockaddr_in serv;
+	SOCKETTYPE sock;
+	int ret = 0;
+	int n;
+	char *buf = NULL;
+	size_t len, p;
+
+	SOCKETINIT;
+
+	ip = gethostbyname(host);
+	if (!ip) {
+		printf("Couldn't get hostname: '%s'\n", host);
+		return 1;
+	}
+
+	sock = socket(AF_INET, SOCK_STREAM, 0);
+	if (sock == INVSOCK) {
+		printf("Socket initialisation failed: %s\n", SOCKERRMSG);
+		return 1;
+	}
+
+	memset(&serv, 0, sizeof(serv));
+	serv.sin_family = AF_INET;
+	serv.sin_addr = *((struct in_addr *)ip->h_addr);
+	serv.sin_port = htons(port);
+
+	if (SOCKETFAIL(connect(sock, (struct sockaddr *)&serv, sizeof(struct sockaddr)))) {
+		printf("Socket connect failed: %s\n", SOCKERRMSG);
+		return 1;
+	}
+
+	n = send(sock, command, strlen(command), 0);
+	if (SOCKETFAIL(n)) {
+		printf("Send failed: %s\n", SOCKERRMSG);
+		ret = 1;
+	}
+	else {
+		len = SOCKSIZ;
+		buf = malloc(len+1);
+		if (!buf) {
+			printf("Err: OOM (%d)\n", (int)(len+1));
+			return 1;
+		}
+		p = 0;
+		while (42) {
+			if ((len - p) < 1) {
+				len += SOCKSIZ;
+				buf = realloc(buf, len+1);
+				if (!buf) {
+					printf("Err: OOM (%d)\n", (int)(len+1));
+					return 1;
+				}
+			}
+
+			n = recv(sock, &buf[p], len - p , 0);
+
+			if (SOCKETFAIL(n)) {
+				printf("Recv failed: %s\n", SOCKERRMSG);
+				ret = 1;
+				break;
+			}
+
+			if (n == 0)
+				break;
+
+			p += n;
+		}
+		buf[p] = '\0';
+
+		if (ONLY)
+			printf("%s\n", buf);
+		else {
+			printf("Reply was '%s'\n", buf);
+			display(buf);
+		}
+	}
+
+	CLOSESOCKET(sock);
+
+	return ret;
+}
+
+static char *trim(char *str)
+{
+	char *ptr;
+
+	while (isspace(*str))
+		str++;
+
+	ptr = strchr(str, '\0');
+	while (ptr-- > str) {
+		if (isspace(*ptr))
+			*ptr = '\0';
+	}
+
+	return str;
+}
+
+int main(int argc, char *argv[])
+{
+	char *command = "summary";
+	char *host = "127.0.0.1";
+	short int port = 4028;
+	char *ptr;
+	int i = 1;
+
+	if (argc > 1)
+		if (strcmp(argv[1], "-?") == 0
+		||  strcmp(argv[1], "-h") == 0
+		||  strcmp(argv[1], "--help") == 0) {
+			fprintf(stderr, "usAge: %s [command [ip/host [port]]]\n", argv[0]);
+			return 1;
+		}
+
+	if (argc > 1)
+		if (strcmp(argv[1], "-o") == 0) {
+			ONLY = 1;
+			i = 2;
+		}
+
+	if (argc > i) {
+		ptr = trim(argv[i++]);
+		if (strlen(ptr) > 0)
+			command = ptr;
+	}
+
+	if (argc > i) {
+		ptr = trim(argv[i++]);
+		if (strlen(ptr) > 0)
+			host = ptr;
+	}
+
+	if (argc > i) {
+		ptr = trim(argv[i]);
+		if (strlen(ptr) > 0)
+			port = atoi(ptr);
+	}
+
+	return callapi(command, host, port);
+}

+ 118 - 0
api-example.php

@@ -0,0 +1,118 @@
+<?php
+#
+# Sample Socket I/O to CGMiner API
+#
+function getsock($addr, $port)
+{
+ $socket = null;
+ $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+ if ($socket === false || $socket === null)
+ {
+	$error = socket_strerror(socket_last_error());
+	$msg = "socket create(TCP) failed";
+	echo "ERR: $msg '$error'\n";
+	return null;
+ }
+
+ $res = socket_connect($socket, $addr, $port);
+ if ($res === false)
+ {
+	$error = socket_strerror(socket_last_error());
+	$msg = "socket connect($addr,$port) failed";
+	echo "ERR: $msg '$error'\n";
+	socket_close($socket);
+	return null;
+ }
+ return $socket;
+}
+#
+# Slow ...
+function readsockline($socket)
+{
+ $line = '';
+ while (true)
+ {
+	$byte = socket_read($socket, 1);
+	if ($byte === false || $byte === '')
+		break;
+	if ($byte === "\0")
+		break;
+	$line .= $byte;
+ }
+ return $line;
+}
+#
+function request($cmd)
+{
+ $socket = getsock('127.0.0.1', 4028);
+ if ($socket != null)
+ {
+	socket_write($socket, $cmd, strlen($cmd));
+	$line = readsockline($socket);
+	socket_close($socket);
+
+	if (strlen($line) == 0)
+	{
+		echo "WARN: '$cmd' returned nothing\n";
+		return $line;
+	}
+
+	print "$cmd returned '$line'\n";
+
+	if (substr($line,0,1) == '{')
+		return json_decode($line, true);
+
+	$data = array();
+
+	$objs = explode('|', $line);
+	foreach ($objs as $obj)
+	{
+		if (strlen($obj) > 0)
+		{
+			$items = explode(',', $obj);
+			$item = $items[0];
+			$id = explode('=', $items[0], 2);
+			if (count($id) == 1 or !ctype_digit($id[1]))
+				$name = $id[0];
+			else
+				$name = $id[0].$id[1];
+
+			if (strlen($name) == 0)
+				$name = 'null';
+
+			if (isset($data[$name]))
+			{
+				$num = 1;
+				while (isset($data[$name.$num]))
+					$num++;
+				$name .= $num;
+			}
+
+			$counter = 0;
+			foreach ($items as $item)
+			{
+				$id = explode('=', $item, 2);
+				if (count($id) == 2)
+					$data[$name][$id[0]] = $id[1];
+				else
+					$data[$name][$counter] = $id[0];
+
+				$counter++;
+			}
+		}
+	}
+
+	return $data;
+ }
+
+ return null;
+}
+#
+if (isset($argv) and count($argv) > 1)
+ $r = request($argv[1]);
+else
+ $r = request('summary');
+#
+echo print_r($r, true)."\n";
+#
+?>

+ 52 - 0
api-example.py

@@ -0,0 +1,52 @@
+#!/usr/bin/env python2.7
+
+# Copyright 2013 Setkeh Mkfr
+#
+# 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.
+
+#Short Python Example for connecting to The Cgminer API
+#Written By: setkeh <https://github.com/setkeh>
+#Thanks to Jezzz for all his Support.
+#NOTE: When adding a param with a pipe | in bash or ZSH you must wrap the arg in quotes
+#E.G "pga|0"
+
+import socket
+import json
+import sys
+
+def linesplit(socket):
+	buffer = socket.recv(4096)
+	done = False
+	while not done:
+		more = socket.recv(4096)
+		if not more:
+			done = True
+		else:
+			buffer = buffer+more
+	if buffer:
+		return buffer
+
+api_command = sys.argv[1].split('|')
+
+if len(sys.argv) < 3:
+	api_ip = '127.0.0.1'
+	api_port = 4028
+else:
+	api_ip = sys.argv[2]
+	api_port = sys.argv[3]
+
+s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
+s.connect((api_ip,int(api_port)))
+if len(api_command) == 2:
+	s.send(json.dumps({"command":api_command[0],"parameter":api_command[1]}))
+else:
+	s.send(json.dumps({"command":api_command[0]}))
+
+response = linesplit(s)
+response = response.replace('\x00','')
+response = json.loads(response)
+print response
+s.close()

+ 38 - 0
api-example.rb

@@ -0,0 +1,38 @@
+#!/usr/bin/env ruby
+
+# Copyright 2014 James Hilliard
+#
+# 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.
+
+require 'socket'
+require 'json'
+
+api_command = ARGV[0].split(":")
+
+if ARGV.length == 3
+	api_ip = ARGV[1]
+	api_port = ARGV[2]
+elsif ARGV.length == 2
+	api_ip = ARGV[1]
+	api_port = 4028
+else
+	api_ip = "127.0.0.1"
+	api_port = 4028
+end
+
+s = TCPSocket.open(api_ip, api_port)
+
+if api_command.count == 2
+	s.write({ :command => api_command[0], :parameter => api_command[1]}.to_json)
+else
+	s.write({ :command => api_command[0]}.to_json)
+end
+
+response = s.read.strip
+response = JSON.parse(response)
+
+puts response
+s.close

+ 5245 - 0
api.c

@@ -0,0 +1,5245 @@
+/*
+ * Copyright 2011-2022 Andrew Smith
+ * Copyright 2011-2015,2018 Con Kolivas
+ *
+ * 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.
+ */
+#define _MEMORY_DEBUG_MASTER 1
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+
+#include "compat.h"
+#include "miner.h"
+#include "util.h"
+#include "klist.h"
+
+#if defined(USE_BFLSC) || defined(USE_AVALON) || defined(USE_AVALON2) || defined(USE_AVALON4) || \
+  defined(USE_HASHFAST) || defined(USE_BITFURY) || defined(USE_BITFURY16) || defined(USE_BLOCKERUPTER) || defined(USE_KLONDIKE) || \
+	defined(USE_KNC) || defined(USE_BAB) || defined(USE_DRAGONMINT_T1) || defined(USE_DRILLBIT) || \
+	defined(USE_MINION) || defined(USE_COINTERRA) || defined(USE_BITMINE_A1) || \
+	defined(USE_ANT_S1) || defined(USE_ANT_S2) || defined(USE_ANT_S3) || defined(USE_SP10) || \
+	defined(USE_SP30) || defined(USE_ICARUS) || defined(USE_HASHRATIO) || defined(USE_AVALON_MINER) || \
+	defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_AVALON9) || defined(USE_AVALONLC3) || defined(USE_BITMAIN_SOC) || defined(USE_GEKKO)
+#define HAVE_AN_ASIC 1
+#endif
+
+#if defined(USE_BITFORCE) || defined(USE_MODMINER)
+#define HAVE_AN_FPGA 1
+#endif
+
+// BUFSIZ varies on Windows and Linux
+#define TMPBUFSIZ	8192
+
+// Number of requests to queue - normally would be small
+// However lots of PGA's may mean more
+#define QUEUE	100
+
+#if defined WIN32
+static char WSAbuf[1024];
+
+struct WSAERRORS {
+	int id;
+	char *code;
+} WSAErrors[] = {
+	{ 0,			"No error" },
+	{ WSAEINTR,		"Interrupted system call" },
+	{ WSAEBADF,		"Bad file number" },
+	{ WSAEACCES,		"Permission denied" },
+	{ WSAEFAULT,		"Bad address" },
+	{ WSAEINVAL,		"Invalid argument" },
+	{ WSAEMFILE,		"Too many open sockets" },
+	{ WSAEWOULDBLOCK,	"Operation would block" },
+	{ WSAEINPROGRESS,	"Operation now in progress" },
+	{ WSAEALREADY,		"Operation already in progress" },
+	{ WSAENOTSOCK,		"Socket operation on non-socket" },
+	{ WSAEDESTADDRREQ,	"Destination address required" },
+	{ WSAEMSGSIZE,		"Message too long" },
+	{ WSAEPROTOTYPE,	"Protocol wrong type for socket" },
+	{ WSAENOPROTOOPT,	"Bad protocol option" },
+	{ WSAEPROTONOSUPPORT,	"Protocol not supported" },
+	{ WSAESOCKTNOSUPPORT,	"Socket type not supported" },
+	{ WSAEOPNOTSUPP,	"Operation not supported on socket" },
+	{ WSAEPFNOSUPPORT,	"Protocol family not supported" },
+	{ WSAEAFNOSUPPORT,	"Address family not supported" },
+	{ WSAEADDRINUSE,	"Address already in use" },
+	{ WSAEADDRNOTAVAIL,	"Can't assign requested address" },
+	{ WSAENETDOWN,		"Network is down" },
+	{ WSAENETUNREACH,	"Network is unreachable" },
+	{ WSAENETRESET,		"Net connection reset" },
+	{ WSAECONNABORTED,	"Software caused connection abort" },
+	{ WSAECONNRESET,	"Connection reset by peer" },
+	{ WSAENOBUFS,		"No buffer space available" },
+	{ WSAEISCONN,		"Socket is already connected" },
+	{ WSAENOTCONN,		"Socket is not connected" },
+	{ WSAESHUTDOWN,		"Can't send after socket shutdown" },
+	{ WSAETOOMANYREFS,	"Too many references, can't splice" },
+	{ WSAETIMEDOUT,		"Connection timed out" },
+	{ WSAECONNREFUSED,	"Connection refused" },
+	{ WSAELOOP,		"Too many levels of symbolic links" },
+	{ WSAENAMETOOLONG,	"File name too long" },
+	{ WSAEHOSTDOWN,		"Host is down" },
+	{ WSAEHOSTUNREACH,	"No route to host" },
+	{ WSAENOTEMPTY,		"Directory not empty" },
+	{ WSAEPROCLIM,		"Too many processes" },
+	{ WSAEUSERS,		"Too many users" },
+	{ WSAEDQUOT,		"Disc quota exceeded" },
+	{ WSAESTALE,		"Stale NFS file handle" },
+	{ WSAEREMOTE,		"Too many levels of remote in path" },
+	{ WSASYSNOTREADY,	"Network system is unavailable" },
+	{ WSAVERNOTSUPPORTED,	"Winsock version out of range" },
+	{ WSANOTINITIALISED,	"WSAStartup not yet called" },
+	{ WSAEDISCON,		"Graceful shutdown in progress" },
+	{ WSAHOST_NOT_FOUND,	"Host not found" },
+	{ WSANO_DATA,		"No host data of that type was found" },
+	{ -1,			"Unknown error code" }
+};
+
+char *WSAErrorMsg(void) {
+	int i;
+	int id = WSAGetLastError();
+
+	/* Assume none of them are actually -1 */
+	for (i = 0; WSAErrors[i].id != -1; i++)
+		if (WSAErrors[i].id == id)
+			break;
+
+	snprintf(WSAbuf, sizeof(WSAbuf),
+		"Socket Error: (%d) %s", id, WSAErrors[i].code);
+
+	return &(WSAbuf[0]);
+}
+#endif
+
+#if defined(__APPLE__) || defined(__FreeBSD__)
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
+#endif
+
+static const char *UNAVAILABLE = " - API will not be available";
+static const char *MUNAVAILABLE = " - API multicast listener will not be available";
+
+static const char *BLANK = "";
+static const char *COMMA = ",";
+#define COMSTR ","
+static const char SEPARATOR = '|';
+#define SEPSTR "|"
+#define CMDJOIN '+'
+#define JOIN_CMD "CMD="
+#define BETWEEN_JOIN SEPSTR
+
+static const char *APIVERSION = "3.7";
+static const char *DEAD = "Dead";
+#if defined(HAVE_AN_ASIC) || defined(HAVE_AN_FPGA)
+static const char *SICK = "Sick";
+static const char *NOSTART = "NoStart";
+static const char *INIT = "Initialising";
+#endif
+static const char *DISABLED = "Disabled";
+static const char *ALIVE = "Alive";
+static const char *REJECTING = "Rejecting";
+static const char *UNKNOWN = "Unknown";
+
+static __maybe_unused const char *NONE = "None";
+
+static const char *YES = "Y";
+static const char *NO = "N";
+static const char *NULLSTR = "(null)";
+
+static const char *TRUESTR = "true";
+static const char *FALSESTR = "false";
+
+static const char *SHA256STR = "sha256";
+
+static const char *DEVICECODE = ""
+#ifdef USE_ANT_S1
+			"ANT "
+#endif
+#ifdef USE_ANT_S2
+			"AS2 "
+#endif
+#ifdef USE_ANT_S3
+			"AS3 "
+#endif
+#ifdef USE_AVALON
+			"AVA "
+#endif
+#ifdef USE_BAB
+			"BaB "
+#endif
+#ifdef USE_BFLSC
+			"BAS "
+#endif
+#ifdef USE_BITFORCE
+			"BFL "
+#endif
+#ifdef USE_BITFURY
+			"BFU "
+#endif
+#ifdef USE_BLOCKERUPTER
+			"BET "
+#endif
+#ifdef USE_DRILLBIT
+			"DRB "
+#endif
+#ifdef USE_DRAGONMINT_T1
+			"DT1 "
+#endif
+#ifdef USE_GEKKO
+			"GSX "
+#endif
+#ifdef USE_HASHFAST
+			"HFA "
+#endif
+#ifdef USE_HASHRATIO
+			"HRO "
+#endif
+#ifdef USE_BITMINE_A1
+			"BA1 "
+#endif
+#ifdef USE_ICARUS
+			"ICA "
+#endif
+#ifdef USE_KNC
+			"KnC "
+#endif
+#ifdef USE_MINION
+			"MBA "
+#endif
+#ifdef USE_MODMINER
+			"MMQ "
+#endif
+#ifdef USE_COINTERRA
+			"CTA "
+#endif
+#ifdef USE_SP10
+			"SPN "
+#endif
+#ifdef USE_SP30
+      "S30 "
+#endif
+
+
+			"";
+
+static const char *OSINFO =
+#if defined(__linux)
+			"Linux";
+#else
+#if defined(__APPLE__)
+			"Apple";
+#else
+#if defined (WIN32)
+			"Windows";
+#else
+#if defined(unix)
+			"Unix";
+#else
+			"Unknown";
+#endif
+#endif
+#endif
+#endif
+
+#define _DEVS		"DEVS"
+#define _POOLS		"POOLS"
+#define _SUMMARY	"SUMMARY"
+#define _STATUS		"STATUS"
+#define _VERSION	"VERSION"
+#define _MINECONFIG	"CONFIG"
+
+#ifdef HAVE_AN_FPGA
+#define _PGA		"PGA"
+#endif
+
+#ifdef HAVE_AN_ASIC
+#define _ASC		"ASC"
+#endif
+
+#define _PGAS		"PGAS"
+#define _ASCS		"ASCS"
+#define _NOTIFY		"NOTIFY"
+#define _DEVDETAILS	"DEVDETAILS"
+#define _BYE		"BYE"
+#define _RESTART	"RESTART"
+#define _MINESTATS	"STATS"
+#define _MINEDEBUG	"DBGSTATS"
+#define _CHECK		"CHECK"
+#define _MINECOIN	"COIN"
+#define _DEBUGSET	"DEBUG"
+#define _SETCONFIG	"SETCONFIG"
+#define _USBSTATS	"USBSTATS"
+#define _LCD		"LCD"
+
+static const char ISJSON = '{';
+#define JSON0		"{"
+#define JSON1		"\""
+#define JSON2		"\":["
+#define JSON3		"]"
+#define JSON4		",\"id\":1"
+// If anyone cares, id=0 for truncated output
+#define JSON4_TRUNCATED	",\"id\":0"
+#define JSON5		"}"
+#define JSON6		"\":"
+
+#define JSON_START	JSON0
+#define JSON_DEVS	JSON1 _DEVS JSON2
+#define JSON_POOLS	JSON1 _POOLS JSON2
+#define JSON_SUMMARY	JSON1 _SUMMARY JSON2
+#define JSON_STATUS	JSON1 _STATUS JSON2
+#define JSON_VERSION	JSON1 _VERSION JSON2
+#define JSON_MINECONFIG	JSON1 _MINECONFIG JSON2
+#define JSON_ACTION	JSON0 JSON1 _STATUS JSON6
+
+#ifdef HAVE_AN_FPGA
+#define JSON_PGA	JSON1 _PGA JSON2
+#endif
+
+#ifdef HAVE_AN_ASIC
+#define JSON_ASC	JSON1 _ASC JSON2
+#endif
+
+#define JSON_PGAS	JSON1 _PGAS JSON2
+#define JSON_ASCS	JSON1 _ASCS JSON2
+#define JSON_NOTIFY	JSON1 _NOTIFY JSON2
+#define JSON_DEVDETAILS	JSON1 _DEVDETAILS JSON2
+#define JSON_BYE	JSON1 _BYE JSON1
+#define JSON_RESTART	JSON1 _RESTART JSON1
+#define JSON_CLOSE	JSON3
+#define JSON_MINESTATS	JSON1 _MINESTATS JSON2
+#define JSON_MINEDEBUG	JSON1 _MINEDEBUG JSON2
+#define JSON_CHECK	JSON1 _CHECK JSON2
+#define JSON_MINECOIN	JSON1 _MINECOIN JSON2
+#define JSON_DEBUGSET	JSON1 _DEBUGSET JSON2
+#define JSON_SETCONFIG	JSON1 _SETCONFIG JSON2
+#define JSON_USBSTATS	JSON1 _USBSTATS JSON2
+#define JSON_LCD	JSON1 _LCD JSON2
+#define JSON_END	JSON4 JSON5
+#define JSON_END_TRUNCATED	JSON4_TRUNCATED JSON5
+#define JSON_BETWEEN_JOIN	","
+
+static const char *JSON_COMMAND = "command";
+static const char *JSON_PARAMETER = "parameter";
+
+#define MSG_POOL 7
+#define MSG_NOPOOL 8
+#define MSG_DEVS 9
+#define MSG_NODEVS 10
+#define MSG_SUMM 11
+#define MSG_INVCMD 14
+#define MSG_MISID 15
+
+#define MSG_VERSION 22
+#define MSG_INVJSON 23
+#define MSG_MISCMD 24
+#define MSG_MISPID 25
+#define MSG_INVPID 26
+#define MSG_SWITCHP 27
+#define MSG_MISVAL 28
+#define MSG_NOADL 29
+#define MSG_INVINT 31
+#define MSG_MINECONFIG 33
+#define MSG_MISFN 42
+#define MSG_BADFN 43
+#define MSG_SAVED 44
+#define MSG_ACCDENY 45
+#define MSG_ACCOK 46
+#define MSG_ENAPOOL 47
+#define MSG_DISPOOL 48
+#define MSG_ALRENAP 49
+#define MSG_ALRDISP 50
+#define MSG_DISLASTP 51
+#define MSG_MISPDP 52
+#define MSG_INVPDP 53
+#define MSG_TOOMANYP 54
+#define MSG_ADDPOOL 55
+
+#ifdef HAVE_AN_FPGA
+#define MSG_PGANON 56
+#define MSG_PGADEV 57
+#define MSG_INVPGA 58
+#endif
+
+#define MSG_NUMPGA 59
+#define MSG_NOTIFY 60
+
+#ifdef HAVE_AN_FPGA
+#define MSG_PGALRENA 61
+#define MSG_PGALRDIS 62
+#define MSG_PGAENA 63
+#define MSG_PGADIS 64
+#define MSG_PGAUNW 65
+#endif
+
+#define MSG_REMLASTP 66
+#define MSG_ACTPOOL 67
+#define MSG_REMPOOL 68
+#define MSG_DEVDETAILS 69
+#define MSG_MINESTATS 70
+#define MSG_MISCHK 71
+#define MSG_CHECK 72
+#define MSG_POOLPRIO 73
+#define MSG_DUPPID 74
+#define MSG_MISBOOL 75
+#define MSG_INVBOOL 76
+#define MSG_FOO 77
+#define MSG_MINECOIN 78
+#define MSG_DEBUGSET 79
+#define MSG_PGAIDENT 80
+#define MSG_PGANOID 81
+#define MSG_SETCONFIG 82
+#define MSG_UNKCON 83
+#define MSG_INVNUM 84
+#define MSG_CONPAR 85
+#define MSG_CONVAL 86
+#define MSG_USBSTA 87
+#define MSG_NOUSTA 88
+
+#ifdef HAVE_AN_FPGA
+#define MSG_MISPGAOPT 89
+#define MSG_PGANOSET 90
+#define MSG_PGAHELP 91
+#define MSG_PGASETOK 92
+#define MSG_PGASETERR 93
+#endif
+
+#define MSG_ZERMIS 94
+#define MSG_ZERINV 95
+#define MSG_ZERSUM 96
+#define MSG_ZERNOSUM 97
+#define MSG_PGAUSBNODEV 98
+#define MSG_INVHPLG 99
+#define MSG_HOTPLUG 100
+#define MSG_DISHPLG 101
+#define MSG_NOHPLG 102
+#define MSG_MISHPLG 103
+
+#define MSG_NUMASC 104
+#ifdef HAVE_AN_ASIC
+#define MSG_ASCNON 105
+#define MSG_ASCDEV 106
+#define MSG_INVASC 107
+#define MSG_ASCLRENA 108
+#define MSG_ASCLRDIS 109
+#define MSG_ASCENA 110
+#define MSG_ASCDIS 111
+#define MSG_ASCUNW 112
+#define MSG_ASCIDENT 113
+#define MSG_ASCNOID 114
+#endif
+#define MSG_ASCUSBNODEV 115
+
+#ifdef HAVE_AN_ASIC
+#define MSG_MISASCOPT 116
+#define MSG_ASCNOSET 117
+#define MSG_ASCHELP 118
+#define MSG_ASCSETOK 119
+#define MSG_ASCSETERR 120
+#endif
+
+#define MSG_INVNEG 121
+#define MSG_SETQUOTA 122
+#define MSG_LOCKOK 123
+#define MSG_LOCKDIS 124
+#define MSG_LCD 125
+
+#define MSG_MINEDEBUG 126
+
+#define MSG_DEPRECATED 127
+
+enum code_severity {
+	SEVERITY_ERR,
+	SEVERITY_WARN,
+	SEVERITY_INFO,
+	SEVERITY_SUCC,
+	SEVERITY_FAIL
+};
+
+enum code_parameters {
+	PARAM_PGA,
+	PARAM_ASC,
+	PARAM_PID,
+	PARAM_PGAMAX,
+	PARAM_ASCMAX,
+	PARAM_PMAX,
+	PARAM_POOLMAX,
+
+// Single generic case: have the code resolve it - see below
+	PARAM_DMAX,
+
+	PARAM_CMD,
+	PARAM_POOL,
+	PARAM_STR,
+	PARAM_BOTH,
+	PARAM_BOOL,
+	PARAM_SET,
+	PARAM_INT,
+	PARAM_NONE
+};
+
+struct CODES {
+	const enum code_severity severity;
+	const int code;
+	const enum code_parameters params;
+	const char *description;
+} codes[] = {
+ { SEVERITY_SUCC,  MSG_POOL,	PARAM_PMAX,	"%d Pool(s)" },
+ { SEVERITY_ERR,   MSG_NOPOOL,	PARAM_NONE,	"No pools" },
+
+ { SEVERITY_SUCC,  MSG_DEVS,	PARAM_DMAX,
+#ifdef HAVE_AN_ASIC
+						"%d ASC(s)"
+#endif
+#if defined(HAVE_AN_ASIC) && defined(HAVE_AN_FPGA)
+						" - "
+#endif
+#ifdef HAVE_AN_FPGA
+						"%d PGA(s)"
+#endif
+ },
+
+ { SEVERITY_ERR,   MSG_NODEVS,	PARAM_NONE,	"No "
+#ifdef HAVE_AN_ASIC
+						"ASCs"
+#endif
+#if defined(HAVE_AN_ASIC) && defined(HAVE_AN_FPGA)
+						"/"
+#endif
+#ifdef HAVE_AN_FPGA
+						"PGAs"
+#endif
+ },
+
+ { SEVERITY_SUCC,  MSG_SUMM,	PARAM_NONE,	"Summary" },
+ { SEVERITY_ERR,   MSG_INVCMD,	PARAM_NONE,	"Invalid command" },
+ { SEVERITY_ERR,   MSG_MISID,	PARAM_NONE,	"Missing device id parameter" },
+#ifdef HAVE_AN_FPGA
+ { SEVERITY_ERR,   MSG_PGANON,	PARAM_NONE,	"No PGAs" },
+ { SEVERITY_SUCC,  MSG_PGADEV,	PARAM_PGA,	"PGA%d" },
+ { SEVERITY_ERR,   MSG_INVPGA,	PARAM_PGAMAX,	"Invalid PGA id %d - range is 0 - %d" },
+ { SEVERITY_INFO,  MSG_PGALRENA,PARAM_PGA,	"PGA %d already enabled" },
+ { SEVERITY_INFO,  MSG_PGALRDIS,PARAM_PGA,	"PGA %d already disabled" },
+ { SEVERITY_INFO,  MSG_PGAENA,	PARAM_PGA,	"PGA %d sent enable message" },
+ { SEVERITY_INFO,  MSG_PGADIS,	PARAM_PGA,	"PGA %d set disable flag" },
+ { SEVERITY_ERR,   MSG_PGAUNW,	PARAM_PGA,	"PGA %d is not flagged WELL, cannot enable" },
+#endif
+ { SEVERITY_SUCC,  MSG_NUMPGA,	PARAM_NONE,	"PGA count" },
+ { SEVERITY_SUCC,  MSG_NUMASC,	PARAM_NONE,	"ASC count" },
+ { SEVERITY_SUCC,  MSG_VERSION,	PARAM_NONE,	"CGMiner versions" },
+ { SEVERITY_ERR,   MSG_INVJSON,	PARAM_NONE,	"Invalid JSON" },
+ { SEVERITY_ERR,   MSG_MISCMD,	PARAM_CMD,	"Missing JSON '%s'" },
+ { SEVERITY_ERR,   MSG_MISPID,	PARAM_NONE,	"Missing pool id parameter" },
+ { SEVERITY_ERR,   MSG_INVPID,	PARAM_POOLMAX,	"Invalid pool id %d - range is 0 - %d" },
+ { SEVERITY_SUCC,  MSG_SWITCHP,	PARAM_POOL,	"Switching to pool %d:'%s'" },
+ { SEVERITY_SUCC,  MSG_MINECONFIG,PARAM_NONE,	"CGMiner config" },
+ { SEVERITY_ERR,   MSG_MISFN,	PARAM_NONE,	"Missing save filename parameter" },
+ { SEVERITY_ERR,   MSG_BADFN,	PARAM_STR,	"Can't open or create save file '%s'" },
+ { SEVERITY_SUCC,  MSG_SAVED,	PARAM_STR,	"Configuration saved to file '%s'" },
+ { SEVERITY_ERR,   MSG_ACCDENY,	PARAM_STR,	"Access denied to '%s' command" },
+ { SEVERITY_SUCC,  MSG_ACCOK,	PARAM_NONE,	"Privileged access OK" },
+ { SEVERITY_SUCC,  MSG_ENAPOOL,	PARAM_POOL,	"Enabling pool %d:'%s'" },
+ { SEVERITY_SUCC,  MSG_POOLPRIO,PARAM_NONE,	"Changed pool priorities" },
+ { SEVERITY_ERR,   MSG_DUPPID,	PARAM_PID,	"Duplicate pool specified %d" },
+ { SEVERITY_SUCC,  MSG_DISPOOL,	PARAM_POOL,	"Disabling pool %d:'%s'" },
+ { SEVERITY_INFO,  MSG_ALRENAP,	PARAM_POOL,	"Pool %d:'%s' already enabled" },
+ { SEVERITY_INFO,  MSG_ALRDISP,	PARAM_POOL,	"Pool %d:'%s' already disabled" },
+ { SEVERITY_ERR,   MSG_DISLASTP,PARAM_POOL,	"Cannot disable last active pool %d:'%s'" },
+ { SEVERITY_ERR,   MSG_MISPDP,	PARAM_NONE,	"Missing addpool details" },
+ { SEVERITY_ERR,   MSG_INVPDP,	PARAM_STR,	"Invalid addpool details '%s'" },
+ { SEVERITY_ERR,   MSG_TOOMANYP,PARAM_NONE,	"Reached maximum number of pools (%d)" },
+ { SEVERITY_SUCC,  MSG_ADDPOOL,	PARAM_POOL,	"Added pool %d: '%s'" },
+ { SEVERITY_ERR,   MSG_REMLASTP,PARAM_POOL,	"Cannot remove last pool %d:'%s'" },
+ { SEVERITY_ERR,   MSG_ACTPOOL, PARAM_POOL,	"Cannot remove active pool %d:'%s'" },
+ { SEVERITY_SUCC,  MSG_REMPOOL, PARAM_BOTH,	"Removed pool %d:'%s'" },
+ { SEVERITY_SUCC,  MSG_NOTIFY,	PARAM_NONE,	"Notify" },
+ { SEVERITY_SUCC,  MSG_DEVDETAILS,PARAM_NONE,	"Device Details" },
+ { SEVERITY_SUCC,  MSG_MINESTATS,PARAM_NONE,	"CGMiner stats" },
+ { SEVERITY_ERR,   MSG_MISCHK,	PARAM_NONE,	"Missing check cmd" },
+ { SEVERITY_SUCC,  MSG_CHECK,	PARAM_NONE,	"Check command" },
+ { SEVERITY_ERR,   MSG_MISBOOL,	PARAM_NONE,	"Missing parameter: true/false" },
+ { SEVERITY_ERR,   MSG_INVBOOL,	PARAM_NONE,	"Invalid parameter should be true or false" },
+ { SEVERITY_SUCC,  MSG_FOO,	PARAM_BOOL,	"Failover-Only set to %s" },
+ { SEVERITY_SUCC,  MSG_MINECOIN,PARAM_NONE,	"CGMiner coin" },
+ { SEVERITY_SUCC,  MSG_DEBUGSET,PARAM_NONE,	"Debug settings" },
+#ifdef HAVE_AN_FPGA
+ { SEVERITY_SUCC,  MSG_PGAIDENT,PARAM_PGA,	"Identify command sent to PGA%d" },
+ { SEVERITY_WARN,  MSG_PGANOID,	PARAM_PGA,	"PGA%d does not support identify" },
+#endif
+ { SEVERITY_SUCC,  MSG_SETCONFIG,PARAM_SET,	"Set config '%s' to %d" },
+ { SEVERITY_ERR,   MSG_UNKCON,	PARAM_STR,	"Unknown config '%s'" },
+ { SEVERITY_ERR,   MSG_DEPRECATED, PARAM_STR,	"Deprecated config option '%s'" },
+ { SEVERITY_ERR,   MSG_INVNUM,	PARAM_BOTH,	"Invalid number (%d) for '%s' range is 0-9999" },
+ { SEVERITY_ERR,   MSG_INVNEG,	PARAM_BOTH,	"Invalid negative number (%d) for '%s'" },
+ { SEVERITY_SUCC,  MSG_SETQUOTA,PARAM_SET,	"Set pool '%s' to quota %d'" },
+ { SEVERITY_ERR,   MSG_CONPAR,	PARAM_NONE,	"Missing config parameters 'name,N'" },
+ { SEVERITY_ERR,   MSG_CONVAL,	PARAM_STR,	"Missing config value N for '%s,N'" },
+ { SEVERITY_SUCC,  MSG_USBSTA,	PARAM_NONE,	"USB Statistics" },
+ { SEVERITY_INFO,  MSG_NOUSTA,	PARAM_NONE,	"No USB Statistics" },
+#ifdef HAVE_AN_FPGA
+ { SEVERITY_ERR,   MSG_MISPGAOPT, PARAM_NONE,	"Missing option after PGA number" },
+ { SEVERITY_WARN,  MSG_PGANOSET, PARAM_PGA,	"PGA %d does not support pgaset" },
+ { SEVERITY_INFO,  MSG_PGAHELP, PARAM_BOTH,	"PGA %d set help: %s" },
+ { SEVERITY_SUCC,  MSG_PGASETOK, PARAM_BOTH,	"PGA %d set OK" },
+ { SEVERITY_ERR,   MSG_PGASETERR, PARAM_BOTH,	"PGA %d set failed: %s" },
+#endif
+ { SEVERITY_ERR,   MSG_ZERMIS,	PARAM_NONE,	"Missing zero parameters" },
+ { SEVERITY_ERR,   MSG_ZERINV,	PARAM_STR,	"Invalid zero parameter '%s'" },
+ { SEVERITY_SUCC,  MSG_ZERSUM,	PARAM_STR,	"Zeroed %s stats with summary" },
+ { SEVERITY_SUCC,  MSG_ZERNOSUM, PARAM_STR,	"Zeroed %s stats without summary" },
+#ifdef USE_USBUTILS
+ { SEVERITY_ERR,   MSG_PGAUSBNODEV, PARAM_PGA,	"PGA%d has no device" },
+ { SEVERITY_ERR,   MSG_ASCUSBNODEV, PARAM_PGA,	"ASC%d has no device" },
+#endif
+ { SEVERITY_ERR,   MSG_INVHPLG,	PARAM_STR,	"Invalid value for hotplug (%s) must be 0..9999" },
+ { SEVERITY_SUCC,  MSG_HOTPLUG,	PARAM_INT,	"Hotplug check set to %ds" },
+ { SEVERITY_SUCC,  MSG_DISHPLG,	PARAM_NONE,	"Hotplug disabled" },
+ { SEVERITY_WARN,  MSG_NOHPLG,	PARAM_NONE,	"Hotplug is not available" },
+ { SEVERITY_ERR,   MSG_MISHPLG,	PARAM_NONE,	"Missing hotplug parameter" },
+#ifdef HAVE_AN_ASIC
+ { SEVERITY_ERR,   MSG_ASCNON,	PARAM_NONE,	"No ASCs" },
+ { SEVERITY_SUCC,  MSG_ASCDEV,	PARAM_ASC,	"ASC%d" },
+ { SEVERITY_ERR,   MSG_INVASC,	PARAM_ASCMAX,	"Invalid ASC id %d - range is 0 - %d" },
+ { SEVERITY_INFO,  MSG_ASCLRENA,PARAM_ASC,	"ASC %d already enabled" },
+ { SEVERITY_INFO,  MSG_ASCLRDIS,PARAM_ASC,	"ASC %d already disabled" },
+ { SEVERITY_INFO,  MSG_ASCENA,	PARAM_ASC,	"ASC %d sent enable message" },
+ { SEVERITY_INFO,  MSG_ASCDIS,	PARAM_ASC,	"ASC %d set disable flag" },
+ { SEVERITY_ERR,   MSG_ASCUNW,	PARAM_ASC,	"ASC %d is not flagged WELL, cannot enable" },
+ { SEVERITY_SUCC,  MSG_ASCIDENT,PARAM_ASC,	"Identify command sent to ASC%d" },
+ { SEVERITY_WARN,  MSG_ASCNOID,	PARAM_ASC,	"ASC%d does not support identify" },
+ { SEVERITY_ERR,   MSG_MISASCOPT, PARAM_NONE,	"Missing option after ASC number" },
+ { SEVERITY_WARN,  MSG_ASCNOSET, PARAM_ASC,	"ASC %d does not support ascset" },
+ { SEVERITY_INFO,  MSG_ASCHELP, PARAM_BOTH,	"ASC %d set help: %s" },
+ { SEVERITY_SUCC,  MSG_ASCSETOK, PARAM_BOTH,	"ASC %d set OK" },
+ { SEVERITY_ERR,   MSG_ASCSETERR, PARAM_BOTH,	"ASC %d set failed: %s" },
+#endif
+ { SEVERITY_SUCC,  MSG_LCD,	PARAM_NONE,	"LCD" },
+ { SEVERITY_SUCC,  MSG_LOCKOK,	PARAM_NONE,	"Lock stats created" },
+ { SEVERITY_WARN,  MSG_LOCKDIS,	PARAM_NONE,	"Lock stats not enabled" },
+ { SEVERITY_FAIL, 0, 0, NULL }
+};
+
+static const char *localaddr = "127.0.0.1";
+
+static int my_thr_id = 0;
+static bool bye;
+
+// Used to control quit restart access to shutdown variables
+static pthread_mutex_t quit_restart_lock;
+
+static bool do_a_quit;
+static bool do_a_restart;
+
+static time_t when = 0;	// when the request occurred
+
+struct IPACCESS {
+	struct in6_addr ip;
+	struct in6_addr mask;
+	char group;
+};
+
+#define GROUP(g) (toupper(g))
+#define PRIVGROUP GROUP('W')
+#define NOPRIVGROUP GROUP('R')
+#define ISPRIVGROUP(g) (GROUP(g) == PRIVGROUP)
+#define GROUPOFFSET(g) (GROUP(g) - GROUP('A'))
+#define VALIDGROUP(g) (GROUP(g) >= GROUP('A') && GROUP(g) <= GROUP('Z'))
+#define COMMANDS(g) (apigroups[GROUPOFFSET(g)].commands)
+#define DEFINEDGROUP(g) (ISPRIVGROUP(g) || COMMANDS(g) != NULL)
+
+struct APIGROUPS {
+	// This becomes a string like: "|cmd1|cmd2|cmd3|" so it's quick to search
+	char *commands;
+} apigroups['Z' - 'A' + 1]; // only A=0 to Z=25 (R: noprivs, W: allprivs)
+
+static struct IPACCESS *ipaccess = NULL;
+static int ips = 0;
+
+struct io_data {
+	size_t siz;
+	char *ptr;
+	char *cur;
+	bool sock;
+	bool close;
+};
+
+struct io_list {
+	struct io_data *io_data;
+	struct io_list *prev;
+	struct io_list *next;
+};
+
+static struct io_list *io_head = NULL;
+
+#define SOCKBUFALLOCSIZ 65536
+
+#define io_new(init) _io_new(init, false)
+#define sock_io_new() _io_new(SOCKBUFALLOCSIZ, true)
+
+#define ALLOC_SBITEMS 2
+#define LIMIT_SBITEMS 0
+
+typedef struct sbitem {
+	char *buf;
+	size_t siz;
+	size_t tot;
+} SBITEM;
+
+// Size to grow tot if exceeded
+#define SBEXTEND 4096
+
+#define DATASB(_item) ((SBITEM *)(_item->data))
+
+static K_LIST *strbufs;
+
+static void io_reinit(struct io_data *io_data)
+{
+	io_data->cur = io_data->ptr;
+	*(io_data->ptr) = '\0';
+	io_data->close = false;
+}
+
+static struct io_data *_io_new(size_t initial, bool socket_buf)
+{
+	struct io_data *io_data;
+	struct io_list *io_list;
+
+	io_data = cgmalloc(sizeof(*io_data));
+	io_data->ptr = cgmalloc(initial);
+	io_data->siz = initial;
+	io_data->sock = socket_buf;
+	io_reinit(io_data);
+
+	io_list = cgmalloc(sizeof(*io_list));
+
+	io_list->io_data = io_data;
+
+	if (io_head) {
+		io_list->next = io_head;
+		io_list->prev = io_head->prev;
+		io_list->next->prev = io_list;
+		io_list->prev->next = io_list;
+	} else {
+		io_list->prev = io_list;
+		io_list->next = io_list;
+		io_head = io_list;
+	}
+
+	return io_data;
+}
+
+static bool io_add(struct io_data *io_data, char *buf)
+{
+	size_t len, dif, tot;
+
+	len = strlen(buf);
+	dif = io_data->cur - io_data->ptr;
+	// send will always have enough space to add the JSON
+	tot = len + 1 + dif + sizeof(JSON_CLOSE) + sizeof(JSON_END);
+
+	if (tot > io_data->siz) {
+		size_t new = io_data->siz + (2 * SOCKBUFALLOCSIZ);
+
+		if (new < tot)
+			new = (2 + (size_t)((float)tot / (float)SOCKBUFALLOCSIZ)) * SOCKBUFALLOCSIZ;
+
+		io_data->ptr = cgrealloc(io_data->ptr, new);
+		io_data->cur = io_data->ptr + dif;
+		io_data->siz = new;
+	}
+
+	memcpy(io_data->cur, buf, len + 1);
+	io_data->cur += len;
+
+	return true;
+}
+
+static bool io_put(struct io_data *io_data, char *buf)
+{
+	io_reinit(io_data);
+	return io_add(io_data, buf);
+}
+
+static void io_close(struct io_data *io_data)
+{
+	io_data->close = true;
+}
+
+static void io_free()
+{
+	struct io_list *io_list, *io_next;
+
+	if (io_head) {
+		io_list = io_head;
+		do {
+			io_next = io_list->next;
+
+			free(io_list->io_data->ptr);
+			free(io_list->io_data);
+			free(io_list);
+
+			io_list = io_next;
+		} while (io_list != io_head);
+
+		io_head = NULL;
+	}
+}
+
+// This is only called when expected to be needed (rarely)
+// i.e. strings outside of the codes control (input from the user)
+static char *escape_string(char *str, bool isjson)
+{
+	char *buf, *ptr;
+	int count;
+
+	count = 0;
+	for (ptr = str; *ptr; ptr++) {
+		switch (*ptr) {
+			case ',':
+			case '|':
+			case '=':
+				if (!isjson)
+					count++;
+				break;
+			case '"':
+				if (isjson)
+					count++;
+				break;
+			case '\\':
+				count++;
+				break;
+		}
+	}
+
+	if (count == 0)
+		return str;
+
+	buf = cgmalloc(strlen(str) + count + 1);
+
+	ptr = buf;
+	while (*str)
+		switch (*str) {
+			case ',':
+			case '|':
+			case '=':
+				if (!isjson)
+					*(ptr++) = '\\';
+				*(ptr++) = *(str++);
+				break;
+			case '"':
+				if (isjson)
+					*(ptr++) = '\\';
+				*(ptr++) = *(str++);
+				break;
+			case '\\':
+				*(ptr++) = '\\';
+				*(ptr++) = *(str++);
+				break;
+			default:
+				*(ptr++) = *(str++);
+				break;
+		}
+
+	*ptr = '\0';
+
+	return buf;
+}
+
+static struct api_data *api_add_extra(struct api_data *root, struct api_data *extra)
+{
+	struct api_data *tmp;
+
+	if (root) {
+		if (extra) {
+			// extra tail
+			tmp = extra->prev;
+
+			// extra prev = root tail
+			extra->prev = root->prev;
+
+			// root tail next = extra
+			root->prev->next = extra;
+
+			// extra tail next = root
+			tmp->next = root;
+
+			// root prev = extra tail
+			root->prev = tmp;
+		}
+	} else
+		root = extra;
+
+	return root;
+}
+
+static struct api_data *api_add_data_full(struct api_data *root, char *name, enum api_data_type type, void *data, bool copy_data)
+{
+	struct api_data *api_data;
+
+	api_data = cgmalloc(sizeof(struct api_data));
+
+	api_data->name = strdup(name);
+	api_data->type = type;
+
+	if (root == NULL) {
+		root = api_data;
+		root->prev = root;
+		root->next = root;
+	} else {
+		api_data->prev = root->prev;
+		root->prev = api_data;
+		api_data->next = root;
+		api_data->prev->next = api_data;
+	}
+
+	api_data->data_was_malloc = copy_data;
+
+	// Avoid crashing on bad data
+	if (data == NULL) {
+		api_data->type = type = API_CONST;
+		data = (void *)NULLSTR;
+		api_data->data_was_malloc = copy_data = false;
+	}
+
+	if (!copy_data)
+		api_data->data = data;
+	else
+		switch(type) {
+			case API_ESCAPE:
+			case API_STRING:
+			case API_CONST:
+				api_data->data = cgmalloc(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 = cgmalloc(4);
+				*(uint8_t *)api_data->data = *(uint8_t *)data;
+				break;
+			case API_INT16:
+				/* Most OSs won't really alloc less than 4 */
+				api_data->data = cgmalloc(4);
+				*(int16_t *)api_data->data = *(int16_t *)data;
+				break;
+			case API_UINT16:
+				/* Most OSs won't really alloc less than 4 */
+				api_data->data = cgmalloc(4);
+				*(uint16_t *)api_data->data = *(uint16_t *)data;
+				break;
+			case API_INT:
+				api_data->data = cgmalloc(sizeof(int));
+				*((int *)(api_data->data)) = *((int *)data);
+				break;
+			case API_UINT:
+				api_data->data = cgmalloc(sizeof(unsigned int));
+				*((unsigned int *)(api_data->data)) = *((unsigned int *)data);
+				break;
+			case API_UINT32:
+				api_data->data = cgmalloc(sizeof(uint32_t));
+				*((uint32_t *)(api_data->data)) = *((uint32_t *)data);
+				break;
+			case API_HEX32:
+				api_data->data = cgmalloc(sizeof(uint32_t));
+				*((uint32_t *)(api_data->data)) = *((uint32_t *)data);
+				break;
+			case API_UINT64:
+				api_data->data = cgmalloc(sizeof(uint64_t));
+				*((uint64_t *)(api_data->data)) = *((uint64_t *)data);
+				break;
+			case API_INT64:
+				api_data->data = cgmalloc(sizeof(int64_t));
+				*((int64_t *)(api_data->data)) = *((int64_t *)data);
+				break;
+			case API_DOUBLE:
+			case API_ELAPSED:
+			case API_MHS:
+			case API_MHTOTAL:
+			case API_UTILITY:
+			case API_FREQ:
+			case API_HS:
+			case API_DIFF:
+			case API_PERCENT:
+				api_data->data = cgmalloc(sizeof(double));
+				*((double *)(api_data->data)) = *((double *)data);
+				break;
+			case API_BOOL:
+				api_data->data = cgmalloc(sizeof(bool));
+				*((bool *)(api_data->data)) = *((bool *)data);
+				break;
+			case API_TIMEVAL:
+				api_data->data = cgmalloc(sizeof(struct timeval));
+				memcpy(api_data->data, data, sizeof(struct timeval));
+				break;
+			case API_TIME:
+				api_data->data = cgmalloc(sizeof(time_t));
+				*(time_t *)(api_data->data) = *((time_t *)data);
+				break;
+			case API_FLOAT:
+			case API_VOLTS:
+			case API_TEMP:
+			case API_AVG:
+				api_data->data = cgmalloc(sizeof(float));
+				*((float *)(api_data->data)) = *((float *)data);
+				break;
+			default:
+				applog(LOG_ERR, "API: unknown1 data type %d ignored", type);
+				api_data->type = API_STRING;
+				api_data->data_was_malloc = false;
+				api_data->data = (void *)UNKNOWN;
+				break;
+		}
+
+	return root;
+}
+
+struct api_data *api_add_escape(struct api_data *root, char *name, char *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_ESCAPE, (void *)data, copy_data);
+}
+
+struct api_data *api_add_string(struct api_data *root, char *name, char *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_STRING, (void *)data, copy_data);
+}
+
+struct api_data *api_add_const(struct api_data *root, char *name, const char *data, bool 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_int16(struct api_data *root, char *name, uint16_t *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_INT16, (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);
+}
+
+struct api_data *api_add_uint(struct api_data *root, char *name, unsigned int *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_UINT, (void *)data, copy_data);
+}
+
+struct api_data *api_add_uint32(struct api_data *root, char *name, uint32_t *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_UINT32, (void *)data, copy_data);
+}
+
+struct api_data *api_add_hex32(struct api_data *root, char *name, uint32_t *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_HEX32, (void *)data, copy_data);
+}
+
+struct api_data *api_add_uint64(struct api_data *root, char *name, uint64_t *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_UINT64, (void *)data, copy_data);
+}
+
+struct api_data *api_add_int64(struct api_data *root, char *name, int64_t *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_INT64, (void *)data, copy_data);
+}
+
+struct api_data *api_add_double(struct api_data *root, char *name, double *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_DOUBLE, (void *)data, copy_data);
+}
+
+struct api_data *api_add_float(struct api_data *root, char *name, float *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_FLOAT, (void *)data, copy_data);
+}
+
+struct api_data *api_add_elapsed(struct api_data *root, char *name, double *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_ELAPSED, (void *)data, copy_data);
+}
+
+struct api_data *api_add_bool(struct api_data *root, char *name, bool *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_BOOL, (void *)data, copy_data);
+}
+
+struct api_data *api_add_timeval(struct api_data *root, char *name, struct timeval *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_TIMEVAL, (void *)data, copy_data);
+}
+
+struct api_data *api_add_time(struct api_data *root, char *name, time_t *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_TIME, (void *)data, copy_data);
+}
+
+struct api_data *api_add_mhs(struct api_data *root, char *name, double *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_MHS, (void *)data, copy_data);
+}
+
+struct api_data *api_add_mhtotal(struct api_data *root, char *name, double *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_MHTOTAL, (void *)data, copy_data);
+}
+
+struct api_data *api_add_temp(struct api_data *root, char *name, float *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_TEMP, (void *)data, copy_data);
+}
+
+struct api_data *api_add_utility(struct api_data *root, char *name, double *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_UTILITY, (void *)data, copy_data);
+}
+
+struct api_data *api_add_freq(struct api_data *root, char *name, double *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_FREQ, (void *)data, copy_data);
+}
+
+struct api_data *api_add_volts(struct api_data *root, char *name, float *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_VOLTS, (void *)data, copy_data);
+}
+
+struct api_data *api_add_hs(struct api_data *root, char *name, double *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_HS, (void *)data, copy_data);
+}
+
+struct api_data *api_add_diff(struct api_data *root, char *name, double *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_DIFF, (void *)data, copy_data);
+}
+
+struct api_data *api_add_percent(struct api_data *root, char *name, double *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_PERCENT, (void *)data, copy_data);
+}
+
+struct api_data *api_add_avg(struct api_data *root, char *name, float *data, bool copy_data)
+{
+	return api_add_data_full(root, name, API_AVG, (void *)data, copy_data);
+}
+
+static void add_item_buf(K_ITEM *item, const char *str)
+{
+	size_t old_siz, new_siz, siz, ext;
+	char *buf;
+
+	buf = DATASB(item)->buf;
+	siz = (size_t)strlen(str);
+
+	old_siz = DATASB(item)->siz;
+	new_siz = old_siz + siz + 1; // include '\0'
+	if (DATASB(item)->tot < new_siz) {
+		ext = (siz + 1) + SBEXTEND - ((siz + 1) % SBEXTEND);
+		DATASB(item)->buf = buf = cgrealloc(DATASB(item)->buf, DATASB(item)->tot + ext);
+		DATASB(item)->tot += ext;
+	}
+	memcpy(buf + old_siz, str, siz + 1);
+	DATASB(item)->siz += siz;
+}
+
+static struct api_data *print_data(struct io_data *io_data, struct api_data *root, bool isjson, bool precom)
+{
+	// N.B. strings don't use this buffer so 64 is enough (for now)
+	char buf[64];
+	struct api_data *tmp;
+	bool done, first = true;
+	char *original, *escape;
+	K_ITEM *item;
+
+	K_WLOCK(strbufs);
+	item = k_unlink_head(strbufs);
+	K_WUNLOCK(strbufs);
+
+	DATASB(item)->siz = 0;
+
+	if (precom)
+		add_item_buf(item, COMMA);
+
+	if (isjson)
+		add_item_buf(item, JSON0);
+
+	while (root) {
+		if (!first)
+			add_item_buf(item, COMMA);
+		else
+			first = false;
+
+		if (isjson)
+			add_item_buf(item, JSON1);
+
+		add_item_buf(item, root->name);
+
+		if (isjson)
+			add_item_buf(item, JSON1);
+
+		if (isjson)
+			add_item_buf(item, ":");
+		else
+			add_item_buf(item, "=");
+
+		first = false;
+
+		done = false;
+		switch(root->type) {
+			case API_STRING:
+			case API_CONST:
+				if (isjson)
+					add_item_buf(item, JSON1);
+				add_item_buf(item, (char *)(root->data));
+				if (isjson)
+					add_item_buf(item, JSON1);
+				done = true;
+				break;
+			case API_ESCAPE:
+				original = (char *)(root->data);
+				escape = escape_string((char *)(root->data), isjson);
+				if (isjson)
+					add_item_buf(item, JSON1);
+				add_item_buf(item, escape);
+				if (isjson)
+					add_item_buf(item, JSON1);
+				if (escape != original)
+					free(escape);
+				done = true;
+				break;
+			case API_UINT8:
+				snprintf(buf, sizeof(buf), "%u", *(uint8_t *)root->data);
+				break;
+			case API_INT16:
+				snprintf(buf, sizeof(buf), "%d", *(int16_t *)root->data);
+				break;
+			case API_UINT16:
+				snprintf(buf, sizeof(buf), "%u", *(uint16_t *)root->data);
+				break;
+			case API_INT:
+				snprintf(buf, sizeof(buf), "%d", *((int *)(root->data)));
+				break;
+			case API_UINT:
+				snprintf(buf, sizeof(buf), "%u", *((unsigned int *)(root->data)));
+				break;
+			case API_UINT32:
+				snprintf(buf, sizeof(buf), "%"PRIu32, *((uint32_t *)(root->data)));
+				break;
+			case API_HEX32:
+				if (isjson)
+					add_item_buf(item, JSON1);
+				snprintf(buf, sizeof(buf), "0x%08x", *((uint32_t *)(root->data)));
+				add_item_buf(item, buf);
+				if (isjson)
+					add_item_buf(item, JSON1);
+				done = true;
+				break;
+			case API_UINT64:
+				snprintf(buf, sizeof(buf), "%"PRIu64, *((uint64_t *)(root->data)));
+				break;
+			case API_INT64:
+				snprintf(buf, sizeof(buf), "%"PRId64, *((int64_t *)(root->data)));
+				break;
+			case API_TIME:
+				snprintf(buf, sizeof(buf), "%lu", *((unsigned long *)(root->data)));
+				break;
+			case API_DOUBLE:
+				snprintf(buf, sizeof(buf), "%f", *((double *)(root->data)));
+				break;
+			case API_FLOAT:
+				snprintf(buf, sizeof(buf), "%f", *((float *)(root->data)));
+				break;
+			case API_ELAPSED:
+				snprintf(buf, sizeof(buf), "%.0f", *((double *)(root->data)));
+				break;
+			case API_UTILITY:
+			case API_FREQ:
+			case API_MHS:
+				snprintf(buf, sizeof(buf), "%.2f", *((double *)(root->data)));
+				break;
+			case API_VOLTS:
+			case API_AVG:
+				snprintf(buf, sizeof(buf), "%.3f", *((float *)(root->data)));
+				break;
+			case API_MHTOTAL:
+				snprintf(buf, sizeof(buf), "%.4f", *((double *)(root->data)));
+				break;
+			case API_HS:
+				snprintf(buf, sizeof(buf), "%.15f", *((double *)(root->data)));
+				break;
+			case API_DIFF:
+				snprintf(buf, sizeof(buf), "%.8f", *((double *)(root->data)));
+				break;
+			case API_BOOL:
+				snprintf(buf, sizeof(buf), "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR);
+				break;
+			case API_TIMEVAL:
+				snprintf(buf, sizeof(buf), "%ld.%06ld",
+					(long)((struct timeval *)(root->data))->tv_sec,
+					(long)((struct timeval *)(root->data))->tv_usec);
+				break;
+			case API_TEMP:
+				snprintf(buf, sizeof(buf), "%.2f", *((float *)(root->data)));
+				break;
+			case API_PERCENT:
+				snprintf(buf, sizeof(buf), "%.4f", *((double *)(root->data)) * 100.0);
+				break;
+			default:
+				applog(LOG_ERR, "API: unknown2 data type %d ignored", root->type);
+				if (isjson)
+					add_item_buf(item, JSON1);
+				add_item_buf(item, UNKNOWN);
+				if (isjson)
+					add_item_buf(item, JSON1);
+				done = true;
+				break;
+		}
+
+		if (!done)
+			add_item_buf(item, buf);
+
+		free(root->name);
+		if (root->data_was_malloc)
+			free(root->data);
+
+		if (root->next == root) {
+			free(root);
+			root = NULL;
+		} else {
+			tmp = root;
+			root = tmp->next;
+			root->prev = tmp->prev;
+			root->prev->next = root;
+			free(tmp);
+		}
+	}
+
+	if (isjson)
+		add_item_buf(item, JSON5);
+	else
+		add_item_buf(item, SEPSTR);
+
+	io_add(io_data, DATASB(item)->buf);
+
+	K_WLOCK(strbufs);
+	k_add_head(strbufs, item);
+	K_WUNLOCK(strbufs);
+
+	return root;
+}
+
+#define DRIVER_COUNT_DRV(X) if (devices[i]->drv->drv_id == DRIVER_##X) \
+	count++;
+
+#ifdef HAVE_AN_ASIC
+static int numascs(void)
+{
+	int count = 0;
+	int i;
+
+	rd_lock(&devices_lock);
+	for (i = 0; i < total_devices; i++) {
+		ASIC_PARSE_COMMANDS(DRIVER_COUNT_DRV)
+	}
+	rd_unlock(&devices_lock);
+	return count;
+}
+
+static int ascdevice(int ascid)
+{
+	int count = 0;
+	int i;
+
+	rd_lock(&devices_lock);
+	for (i = 0; i < total_devices; i++) {
+		ASIC_PARSE_COMMANDS(DRIVER_COUNT_DRV)
+		if (count == (ascid + 1))
+			goto foundit;
+	}
+
+	rd_unlock(&devices_lock);
+	return -1;
+
+foundit:
+
+	rd_unlock(&devices_lock);
+	return i;
+}
+#endif
+
+#ifdef HAVE_AN_FPGA
+static int numpgas(void)
+{
+	int count = 0;
+	int i;
+
+	rd_lock(&devices_lock);
+	for (i = 0; i < total_devices; i++) {
+		FPGA_PARSE_COMMANDS(DRIVER_COUNT_DRV)
+	}
+	rd_unlock(&devices_lock);
+	return count;
+}
+
+static int pgadevice(int pgaid)
+{
+	int count = 0;
+	int i;
+
+	rd_lock(&devices_lock);
+	for (i = 0; i < total_devices; i++) {
+		FPGA_PARSE_COMMANDS(DRIVER_COUNT_DRV)
+		if (count == (pgaid + 1))
+			goto foundit;
+	}
+
+	rd_unlock(&devices_lock);
+	return -1;
+
+foundit:
+
+	rd_unlock(&devices_lock);
+	return i;
+}
+#endif
+
+#define LIMSIZ (TMPBUFSIZ - 1)
+
+// All replies (except BYE and RESTART) start with a message
+//  thus for JSON, message() inserts JSON_START at the front
+//  and send_result() adds JSON_END at the end
+static void message(struct io_data *io_data, int messageid, int paramid, char *param2, bool isjson)
+{
+	struct api_data *root = NULL;
+	char buf[TMPBUFSIZ];
+	char severity[2];
+#ifdef HAVE_AN_ASIC
+	int asc;
+#endif
+#ifdef HAVE_AN_FPGA
+	int pga;
+#endif
+	int i;
+
+	if (isjson)
+		io_add(io_data, JSON_START JSON_STATUS);
+
+	for (i = 0; codes[i].severity != SEVERITY_FAIL; i++) {
+		if (codes[i].code == messageid) {
+			switch (codes[i].severity) {
+				case SEVERITY_WARN:
+					severity[0] = 'W';
+					break;
+				case SEVERITY_INFO:
+					severity[0] = 'I';
+					break;
+				case SEVERITY_SUCC:
+					severity[0] = 'S';
+					break;
+				case SEVERITY_ERR:
+				default:
+					severity[0] = 'E';
+					break;
+			}
+			severity[1] = '\0';
+
+			switch(codes[i].params) {
+				case PARAM_PGA:
+				case PARAM_ASC:
+				case PARAM_PID:
+				case PARAM_INT:
+					snprintf(buf, LIMSIZ, codes[i].description, paramid);
+					break;
+				case PARAM_POOL:
+					snprintf(buf, LIMSIZ, codes[i].description, paramid, pools[paramid]->rpc_url);
+					break;
+#ifdef HAVE_AN_FPGA
+				case PARAM_PGAMAX:
+					pga = numpgas();
+					snprintf(buf, LIMSIZ, codes[i].description, paramid, pga - 1);
+					break;
+#endif
+#ifdef HAVE_AN_ASIC
+				case PARAM_ASCMAX:
+					asc = numascs();
+					snprintf(buf, LIMSIZ, codes[i].description, paramid, asc - 1);
+					break;
+#endif
+				case PARAM_PMAX:
+					snprintf(buf, LIMSIZ, codes[i].description, total_pools);
+					break;
+				case PARAM_POOLMAX:
+					snprintf(buf, LIMSIZ, codes[i].description, paramid, total_pools - 1);
+					break;
+				case PARAM_DMAX:
+#ifdef HAVE_AN_ASIC
+					asc = numascs();
+#endif
+#ifdef HAVE_AN_FPGA
+					pga = numpgas();
+#endif
+
+					snprintf(buf, LIMSIZ, codes[i].description
+#ifdef HAVE_AN_ASIC
+						, asc
+#endif
+#ifdef HAVE_AN_FPGA
+						, pga
+#endif
+#if !defined(HAVE_AN_ASIC) || !defined(HAVE_AN_FPGA)
+						, NULL //remove error if compile without driver (needed for openwrt tests)
+#endif
+					);
+					break;
+				case PARAM_CMD:
+					snprintf(buf, LIMSIZ, codes[i].description, JSON_COMMAND);
+					break;
+				case PARAM_STR:
+					snprintf(buf, LIMSIZ, codes[i].description, param2);
+					break;
+				case PARAM_BOTH:
+					snprintf(buf, LIMSIZ, codes[i].description, paramid, param2);
+					break;
+				case PARAM_BOOL:
+					snprintf(buf, LIMSIZ, codes[i].description, paramid ? TRUESTR : FALSESTR);
+					break;
+				case PARAM_SET:
+					snprintf(buf, LIMSIZ, codes[i].description, param2, paramid);
+					break;
+				case PARAM_NONE:
+				default:
+					strcpy(buf, codes[i].description);
+			}
+
+			root = api_add_string(root, _STATUS, severity, false);
+			root = api_add_time(root, "When", &when, false);
+			root = api_add_int(root, "Code", &messageid, false);
+			root = api_add_escape(root, "Msg", buf, false);
+			/* Do not give out description for random probes to
+			 * addresses with inappropriately open API ports. */
+			if (messageid != MSG_INVCMD)
+				root = api_add_escape(root, "Description", opt_api_description, false);
+
+			root = print_data(io_data, root, isjson, false);
+			if (isjson)
+				io_add(io_data, JSON_CLOSE);
+			return;
+		}
+	}
+
+	root = api_add_string(root, _STATUS, "F", false);
+	root = api_add_time(root, "When", &when, false);
+	int id = -1;
+	root = api_add_int(root, "Code", &id, false);
+	snprintf(buf, sizeof(buf), "%d", messageid);
+	root = api_add_escape(root, "Msg", buf, false);
+	root = api_add_escape(root, "Description", opt_api_description, false);
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson)
+		io_add(io_data, JSON_CLOSE);
+}
+
+#if LOCK_TRACKING
+
+#define LOCK_FMT_FFL " - called from %s %s():%d"
+
+#define LOCKMSG(fmt, ...)	fprintf(stderr, "APILOCK: " fmt "\n", ##__VA_ARGS__)
+#define LOCKMSGMORE(fmt, ...)	fprintf(stderr, "          " fmt "\n", ##__VA_ARGS__)
+#define LOCKMSGFFL(fmt, ...) fprintf(stderr, "APILOCK: " fmt LOCK_FMT_FFL "\n", ##__VA_ARGS__, file, func, linenum)
+#define LOCKMSGFLUSH() fflush(stderr)
+
+typedef struct lockstat {
+	uint64_t lock_id;
+	const char *file;
+	const char *func;
+	int linenum;
+	struct timeval tv;
+} LOCKSTAT;
+
+typedef struct lockline {
+	struct lockline *prev;
+	struct lockstat *stat;
+	struct lockline *next;
+} LOCKLINE;
+
+typedef struct lockinfo {
+	void *lock;
+	enum cglock_typ typ;
+	const char *file;
+	const char *func;
+	int linenum;
+	uint64_t gets;
+	uint64_t gots;
+	uint64_t tries;
+	uint64_t dids;
+	uint64_t didnts; // should be tries - dids
+	uint64_t unlocks;
+	LOCKSTAT lastgot;
+	LOCKLINE *lockgets;
+	LOCKLINE *locktries;
+} LOCKINFO;
+
+typedef struct locklist {
+	LOCKINFO *info;
+	struct locklist *next;
+} LOCKLIST;
+
+static uint64_t lock_id = 1;
+
+static LOCKLIST *lockhead;
+
+static void lockmsgnow()
+{
+	struct timeval now;
+	struct tm *tm;
+	time_t dt;
+
+	cgtime(&now);
+
+	dt = now.tv_sec;
+	tm = localtime(&dt);
+
+	LOCKMSG("%d-%02d-%02d %02d:%02d:%02d",
+		tm->tm_year + 1900,
+		tm->tm_mon + 1,
+		tm->tm_mday,
+		tm->tm_hour,
+		tm->tm_min,
+		tm->tm_sec);
+}
+
+static LOCKLIST *newlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum)
+{
+	LOCKLIST *list;
+
+	list = cgcalloc(1, sizeof(*list));
+	list->info = cgcalloc(1, sizeof(*(list->info)));
+	list->next = lockhead;
+	lockhead = list;
+
+	list->info->lock = lock;
+	list->info->typ = typ;
+	list->info->file = file;
+	list->info->func = func;
+	list->info->linenum = linenum;
+
+	return list;
+}
+
+static LOCKINFO *findlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum)
+{
+	LOCKLIST *look;
+
+	look = lockhead;
+	while (look) {
+		if (look->info->lock == lock)
+			break;
+		look = look->next;
+	}
+
+	if (!look)
+		look = newlock(lock, typ, file, func, linenum);
+
+	return look->info;
+}
+
+static void addgettry(LOCKINFO *info, uint64_t id, const char *file, const char *func, const int linenum, bool get)
+{
+	LOCKSTAT *stat;
+	LOCKLINE *line;
+
+	stat = cgcalloc(1, sizeof(*stat));
+	line = cgcalloc(1, sizeof(*line));
+
+	if (get)
+		info->gets++;
+	else
+		info->tries++;
+
+	stat->lock_id = id;
+	stat->file = file;
+	stat->func = func;
+	stat->linenum = linenum;
+	cgtime(&stat->tv);
+
+	line->stat = stat;
+
+	if (get) {
+		line->next = info->lockgets;
+		if (info->lockgets)
+			info->lockgets->prev = line;
+		info->lockgets = line;
+	} else {
+		line->next = info->locktries;
+		if (info->locktries)
+			info->locktries->prev = line;
+		info->locktries = line;
+	}
+}
+
+static void markgotdid(LOCKINFO *info, uint64_t id, const char *file, const char *func, const int linenum, bool got, int ret)
+{
+	LOCKLINE *line;
+
+	if (got)
+		info->gots++;
+	else {
+		if (ret == 0)
+			info->dids++;
+		else
+			info->didnts++;
+	}
+
+	if (got || ret == 0) {
+		info->lastgot.lock_id = id;
+		info->lastgot.file = file;
+		info->lastgot.func = func;
+		info->lastgot.linenum = linenum;
+		cgtime(&info->lastgot.tv);
+	}
+
+	if (got)
+		line = info->lockgets;
+	else
+		line = info->locktries;
+	while (line) {
+		if (line->stat->lock_id == id)
+			break;
+		line = line->next;
+	}
+
+	if (!line) {
+		lockmsgnow();
+		LOCKMSGFFL("ERROR attempt to mark a lock as '%s' that wasn't '%s' id=%"PRIu64,
+				got ? "got" : "did/didnt", got ? "get" : "try", id);
+	}
+
+	// Unlink it
+	if (line->prev)
+		line->prev->next = line->next;
+	if (line->next)
+		line->next->prev = line->prev;
+
+	if (got) {
+		if (info->lockgets == line)
+			info->lockgets = line->next;
+	} else {
+		if (info->locktries == line)
+			info->locktries = line->next;
+	}
+
+	free(line->stat);
+	free(line);
+}
+
+// Yes this uses locks also ... ;/
+static void locklock()
+{
+	if (unlikely(pthread_mutex_lock(&lockstat_lock)))
+		quithere(1, "WTF MUTEX ERROR ON LOCK! errno=%d", errno);
+}
+
+static void lockunlock()
+{
+	if (unlikely(pthread_mutex_unlock(&lockstat_lock)))
+		quithere(1, "WTF MUTEX ERROR ON UNLOCK! errno=%d", errno);
+}
+
+uint64_t api_getlock(void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+	uint64_t id;
+
+	locklock();
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	id = lock_id++;
+	addgettry(info, id, file, func, linenum, true);
+
+	lockunlock();
+
+	return id;
+}
+
+void api_gotlock(uint64_t id, void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+
+	locklock();
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	markgotdid(info, id, file, func, linenum, true, 0);
+
+	lockunlock();
+}
+
+uint64_t api_trylock(void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+	uint64_t id;
+
+	locklock();
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	id = lock_id++;
+	addgettry(info, id, file, func, linenum, false);
+
+	lockunlock();
+
+	return id;
+}
+
+void api_didlock(uint64_t id, int ret, void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+
+	locklock();
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	markgotdid(info, id, file, func, linenum, false, ret);
+
+	lockunlock();
+}
+
+void api_gunlock(void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+
+	locklock();
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	info->unlocks++;
+
+	lockunlock();
+}
+
+void api_initlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum)
+{
+	locklock();
+
+	findlock(lock, typ, file, func, linenum);
+
+	lockunlock();
+}
+
+void dsp_det(char *msg, LOCKSTAT *stat)
+{
+	struct tm *tm;
+	time_t dt;
+
+	dt = stat->tv.tv_sec;
+	tm = localtime(&dt);
+
+	LOCKMSGMORE("%s id=%"PRIu64" by %s %s():%d at %d-%02d-%02d %02d:%02d:%02d",
+			msg,
+			stat->lock_id,
+			stat->file,
+			stat->func,
+			stat->linenum,
+			tm->tm_year + 1900,
+			tm->tm_mon + 1,
+			tm->tm_mday,
+			tm->tm_hour,
+			tm->tm_min,
+			tm->tm_sec);
+}
+
+void dsp_lock(LOCKINFO *info)
+{
+	LOCKLINE *line;
+	char *status;
+
+	LOCKMSG("Lock %p created by %s %s():%d",
+		info->lock,
+		info->file,
+		info->func,
+		info->linenum);
+	LOCKMSGMORE("gets:%"PRIu64" gots:%"PRIu64" tries:%"PRIu64
+		    " dids:%"PRIu64" didnts:%"PRIu64" unlocks:%"PRIu64,
+			info->gets,
+			info->gots,
+			info->tries,
+			info->dids,
+			info->didnts,
+			info->unlocks);
+
+	if (info->gots > 0 || info->dids > 0) {
+		if (info->unlocks < info->gots + info->dids)
+			status = "Last got/did still HELD";
+		else
+			status = "Last got/did (idle)";
+
+		dsp_det(status, &(info->lastgot));
+	} else
+		LOCKMSGMORE("... unused ...");
+
+	if (info->lockgets) {
+		LOCKMSGMORE("BLOCKED gets (%"PRIu64")", info->gets - info->gots);
+		line = info->lockgets;
+		while (line) {
+			dsp_det("", line->stat);
+			line = line->next;
+		}
+	} else
+		LOCKMSGMORE("no blocked gets");
+
+	if (info->locktries) {
+		LOCKMSGMORE("BLOCKED tries (%"PRIu64")", info->tries - info->dids - info->didnts);
+		line = info->lockgets;
+		while (line) {
+			dsp_det("", line->stat);
+			line = line->next;
+		}
+	} else
+		LOCKMSGMORE("no blocked tries");
+}
+
+void show_locks()
+{
+	LOCKLIST *list;
+
+	locklock();
+
+	lockmsgnow();
+
+	list = lockhead;
+	if (!list)
+		LOCKMSG("no locks?!?\n");
+	else {
+		while (list) {
+			dsp_lock(list->info);
+			list = list->next;
+		}
+	}
+
+	LOCKMSGFLUSH();
+
+	lockunlock();
+}
+#endif
+
+static void lockstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+#if LOCK_TRACKING
+	show_locks();
+	message(io_data, MSG_LOCKOK, 0, NULL, isjson);
+#else
+	message(io_data, MSG_LOCKDIS, 0, NULL, isjson);
+#endif
+}
+
+static void apiversion(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	bool io_open;
+
+	message(io_data, MSG_VERSION, 0, NULL, isjson);
+	io_open = io_add(io_data, isjson ? COMSTR JSON_VERSION : _VERSION COMSTR);
+
+	root = api_add_string(root, "CGMiner", VERSION, false);
+	root = api_add_const(root, "API", APIVERSION, false);
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void minerconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	bool io_open;
+	int asccount = 0;
+	int pgacount = 0;
+
+#ifdef HAVE_AN_ASIC
+	asccount = numascs();
+#endif
+
+#ifdef HAVE_AN_FPGA
+	pgacount = numpgas();
+#endif
+
+	message(io_data, MSG_MINECONFIG, 0, NULL, isjson);
+	io_open = io_add(io_data, isjson ? COMSTR JSON_MINECONFIG : _MINECONFIG COMSTR);
+
+	root = api_add_int(root, "ASC Count", &asccount, false);
+	root = api_add_int(root, "PGA Count", &pgacount, false);
+	root = api_add_int(root, "Pool Count", &total_pools, false);
+	root = api_add_const(root, "Strategy", strategies[pool_strategy].s, false);
+	root = api_add_int(root, "Log Interval", &opt_log_interval, false);
+	root = api_add_const(root, "Device Code", DEVICECODE, false);
+	root = api_add_const(root, "OS", OSINFO, false);
+#ifdef USE_USBUTILS
+	if (hotplug_time == 0)
+		root = api_add_const(root, "Hotplug", DISABLED, false);
+	else
+		root = api_add_int(root, "Hotplug", &hotplug_time, false);
+#else
+	root = api_add_const(root, "Hotplug", NONE, false);
+#endif
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+#if defined(HAVE_AN_ASIC) || defined(HAVE_AN_FPGA)
+static const char *status2str(enum alive status)
+{
+	switch (status) {
+		case LIFE_WELL:
+			return ALIVE;
+		case LIFE_SICK:
+			return SICK;
+		case LIFE_DEAD:
+			return DEAD;
+		case LIFE_NOSTART:
+			return NOSTART;
+		case LIFE_INIT:
+			return INIT;
+		default:
+			return UNKNOWN;
+	}
+}
+#endif
+
+#ifdef HAVE_AN_ASIC
+static void ascstatus(struct io_data *io_data, int asc, bool isjson, bool precom)
+{
+	struct api_data *root = NULL;
+	char *enabled;
+	char *status;
+	int numasc = numascs();
+
+	if (numasc > 0 && asc >= 0 && asc < numasc) {
+		int dev = ascdevice(asc);
+		if (dev < 0) // Should never happen
+			return;
+
+		struct cgpu_info *cgpu = get_devices(dev);
+		float temp = cgpu->temp;
+		double dev_runtime;
+
+		dev_runtime = cgpu_runtime(cgpu);
+
+		cgpu->utility = cgpu->accepted / dev_runtime * 60;
+
+		if (cgpu->deven != DEV_DISABLED)
+			enabled = (char *)YES;
+		else
+			enabled = (char *)NO;
+
+		status = (char *)status2str(cgpu->status);
+
+		root = api_add_int(root, "ASC", &asc, false);
+		root = api_add_string(root, "Name", cgpu->drv->name, false);
+		root = api_add_int(root, "ID", &(cgpu->device_id), false);
+		root = api_add_string(root, "Enabled", enabled, false);
+		root = api_add_string(root, "Status", status, false);
+		root = api_add_temp(root, "Temperature", &temp, false);
+		double mhs = cgpu->total_mhashes / dev_runtime;
+		root = api_add_mhs(root, "MHS av", &mhs, false);
+		char mhsname[27];
+		snprintf(mhsname, sizeof(mhsname), "MHS %ds", opt_log_interval);
+		root = api_add_mhs(root, mhsname, &(cgpu->rolling), false);
+		root = api_add_mhs(root, "MHS 1m", &cgpu->rolling1, false);
+		root = api_add_mhs(root, "MHS 5m", &cgpu->rolling5, false);
+		root = api_add_mhs(root, "MHS 15m", &cgpu->rolling15, false);
+		root = api_add_int(root, "Accepted", &(cgpu->accepted), false);
+		root = api_add_int(root, "Rejected", &(cgpu->rejected), false);
+		root = api_add_int(root, "Hardware Errors", &(cgpu->hw_errors), false);
+		root = api_add_utility(root, "Utility", &(cgpu->utility), false);
+		int last_share_pool = cgpu->last_share_pool_time > 0 ?
+					cgpu->last_share_pool : -1;
+		root = api_add_int(root, "Last Share Pool", &last_share_pool, false);
+		root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false);
+		root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false);
+		root = api_add_int64(root, "Diff1 Work", &(cgpu->diff1), false);
+		root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false);
+		root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false);
+		root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false);
+#ifdef USE_USBUTILS
+		root = api_add_bool(root, "No Device", &(cgpu->usbinfo.nodev), false);
+#endif
+		root = api_add_time(root, "Last Valid Work", &(cgpu->last_device_valid_work), false);
+		double hwp = (cgpu->hw_errors + cgpu->diff1) ?
+				(double)(cgpu->hw_errors) / (double)(cgpu->hw_errors + cgpu->diff1) : 0;
+		root = api_add_percent(root, "Device Hardware%", &hwp, false);
+		double rejp = cgpu->diff1 ?
+				(double)(cgpu->diff_rejected) / (double)(cgpu->diff1) : 0;
+		root = api_add_percent(root, "Device Rejected%", &rejp, false);
+		root = api_add_elapsed(root, "Device Elapsed", &(dev_runtime), false);
+
+		root = print_data(io_data, root, isjson, precom);
+	}
+}
+#endif
+
+#ifdef HAVE_AN_FPGA
+static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom)
+{
+	struct api_data *root = NULL;
+	char *enabled;
+	char *status;
+	int numpga = numpgas();
+
+	if (numpga > 0 && pga >= 0 && pga < numpga) {
+		int dev = pgadevice(pga);
+		if (dev < 0) // Should never happen
+			return;
+
+		struct cgpu_info *cgpu = get_devices(dev);
+		double frequency = 0;
+		float temp = cgpu->temp;
+		struct timeval now;
+		double dev_runtime;
+
+		if (cgpu->dev_start_tv.tv_sec == 0)
+			dev_runtime = total_secs;
+		else {
+			cgtime(&now);
+			dev_runtime = tdiff(&now, &(cgpu->dev_start_tv));
+		}
+
+		if (dev_runtime < 1.0)
+			dev_runtime = 1.0;
+
+#ifdef USE_MODMINER
+		if (cgpu->drv->drv_id == DRIVER_modminer)
+			frequency = cgpu->clock;
+#endif
+
+		cgpu->utility = cgpu->accepted / dev_runtime * 60;
+
+		if (cgpu->deven != DEV_DISABLED)
+			enabled = (char *)YES;
+		else
+			enabled = (char *)NO;
+
+		status = (char *)status2str(cgpu->status);
+
+		root = api_add_int(root, "PGA", &pga, false);
+		root = api_add_string(root, "Name", cgpu->drv->name, false);
+		root = api_add_int(root, "ID", &(cgpu->device_id), false);
+		root = api_add_string(root, "Enabled", enabled, false);
+		root = api_add_string(root, "Status", status, false);
+		root = api_add_temp(root, "Temperature", &temp, false);
+		double mhs = cgpu->total_mhashes / dev_runtime;
+		root = api_add_mhs(root, "MHS av", &mhs, false);
+		char mhsname[27];
+		snprintf(mhsname, sizeof(mhsname), "MHS %ds", opt_log_interval);
+		root = api_add_mhs(root, mhsname, &(cgpu->rolling), false);
+		root = api_add_mhs(root, "MHS 1m", &cgpu->rolling1, false);
+		root = api_add_mhs(root, "MHS 5m", &cgpu->rolling5, false);
+		root = api_add_mhs(root, "MHS 15m", &cgpu->rolling15, false);
+		root = api_add_int(root, "Accepted", &(cgpu->accepted), false);
+		root = api_add_int(root, "Rejected", &(cgpu->rejected), false);
+		root = api_add_int(root, "Hardware Errors", &(cgpu->hw_errors), false);
+		root = api_add_utility(root, "Utility", &(cgpu->utility), false);
+		int last_share_pool = cgpu->last_share_pool_time > 0 ?
+					cgpu->last_share_pool : -1;
+		root = api_add_int(root, "Last Share Pool", &last_share_pool, false);
+		root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false);
+		root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false);
+		root = api_add_freq(root, "Frequency", &frequency, false);
+		root = api_add_int64(root, "Diff1 Work", &(cgpu->diff1), false);
+		root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false);
+		root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false);
+		root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false);
+#ifdef USE_USBUTILS
+		root = api_add_bool(root, "No Device", &(cgpu->usbinfo.nodev), false);
+#endif
+		root = api_add_time(root, "Last Valid Work", &(cgpu->last_device_valid_work), false);
+		double hwp = (cgpu->hw_errors + cgpu->diff1) ?
+				(double)(cgpu->hw_errors) / (double)(cgpu->hw_errors + cgpu->diff1) : 0;
+		root = api_add_percent(root, "Device Hardware%", &hwp, false);
+		double rejp = cgpu->diff1 ?
+				(double)(cgpu->diff_rejected) / (double)(cgpu->diff1) : 0;
+		root = api_add_percent(root, "Device Rejected%", &rejp, false);
+		root = api_add_elapsed(root, "Device Elapsed", &(dev_runtime), false);
+
+		root = print_data(io_data, root, isjson, precom);
+	}
+}
+#endif
+
+static void devstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	bool io_open = false;
+	int numasc = 0;
+	int numpga = 0;
+#if defined(HAVE_AN_ASIC) || defined(HAVE_AN_FPGA)
+	int devcount = 0;
+	int i;
+#endif
+
+#ifdef HAVE_AN_ASIC
+	numasc = numascs();
+#endif
+
+#ifdef HAVE_AN_FPGA
+	numpga = numpgas();
+#endif
+
+	if (numpga == 0 && numasc == 0) {
+		message(io_data, MSG_NODEVS, 0, NULL, isjson);
+		return;
+	}
+
+
+	message(io_data, MSG_DEVS, 0, NULL, isjson);
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_DEVS);
+
+#ifdef HAVE_AN_ASIC
+	if (numasc > 0) {
+		for (i = 0; i < numasc; i++) {
+			ascstatus(io_data, i, isjson, isjson && devcount > 0);
+
+			devcount++;
+		}
+	}
+#endif
+
+#ifdef HAVE_AN_FPGA
+	if (numpga > 0) {
+		for (i = 0; i < numpga; i++) {
+			pgastatus(io_data, i, isjson, isjson && devcount > 0);
+
+			devcount++;
+		}
+	}
+#endif
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void edevstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	bool io_open = false;
+	int numasc = 0;
+	int numpga = 0;
+#if defined(HAVE_AN_ASIC) || defined(HAVE_AN_FPGA)
+	int devcount = 0;
+	int i;
+#endif
+#ifdef USE_USBUTILS
+	time_t howoldsec = 0;
+#endif
+
+#ifdef HAVE_AN_ASIC
+	numasc = numascs();
+#endif
+
+#ifdef HAVE_AN_FPGA
+	numpga = numpgas();
+#endif
+
+	if (numpga == 0 && numasc == 0) {
+		message(io_data, MSG_NODEVS, 0, NULL, isjson);
+		return;
+	}
+
+#ifdef USE_USBUTILS
+	if (param && *param)
+		howoldsec = (time_t)atoi(param);
+#endif
+
+	message(io_data, MSG_DEVS, 0, NULL, isjson);
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_DEVS);
+
+#ifdef HAVE_AN_ASIC
+	if (numasc > 0) {
+		for (i = 0; i < numasc; i++) {
+#ifdef USE_USBUTILS
+			int dev = ascdevice(i);
+			if (dev < 0) // Should never happen
+				continue;
+
+			struct cgpu_info *cgpu = get_devices(dev);
+			if (!cgpu)
+				continue;
+			if (cgpu->blacklisted)
+				continue;
+			if (cgpu->usbinfo.nodev) {
+				if (howoldsec <= 0)
+					continue;
+				if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec)
+					continue;
+			}
+#endif
+
+			ascstatus(io_data, i, isjson, isjson && devcount > 0);
+
+			devcount++;
+		}
+	}
+#endif
+
+#ifdef HAVE_AN_FPGA
+	if (numpga > 0) {
+		for (i = 0; i < numpga; i++) {
+#ifdef USE_USBUTILS
+			int dev = pgadevice(i);
+			if (dev < 0) // Should never happen
+				continue;
+
+			struct cgpu_info *cgpu = get_devices(dev);
+			if (!cgpu)
+				continue;
+			if (cgpu->blacklisted)
+				continue;
+			if (cgpu->usbinfo.nodev) {
+				if (howoldsec <= 0)
+					continue;
+				if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec)
+					continue;
+			}
+#endif
+
+			pgastatus(io_data, i, isjson, isjson && devcount > 0);
+
+			devcount++;
+		}
+	}
+#endif
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+#ifdef HAVE_AN_FPGA
+static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	bool io_open = false;
+	int numpga = numpgas();
+	int id;
+
+	if (numpga == 0) {
+		message(io_data, MSG_PGANON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= numpga) {
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	message(io_data, MSG_PGADEV, id, NULL, isjson);
+
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_PGA);
+
+	pgastatus(io_data, id, isjson, false);
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	int numpga = numpgas();
+	struct thr_info *thr;
+	int pga;
+	int id;
+	int i;
+
+	if (numpga == 0) {
+		message(io_data, MSG_PGANON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= numpga) {
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	int dev = pgadevice(id);
+	if (dev < 0) { // Should never happen
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	cgpu = get_devices(dev);
+
+	applog(LOG_DEBUG, "API: request to pgaenable pgaid %d device %d %s%u",
+			id, dev, cgpu->drv->name, cgpu->device_id);
+
+	if (cgpu->deven != DEV_DISABLED) {
+		message(io_data, MSG_PGALRENA, id, NULL, isjson);
+		return;
+	}
+
+#if 0 /* A DISABLED device wont change status FIXME: should disabling make it WELL? */
+	if (cgpu->status != LIFE_WELL) {
+		message(io_data, MSG_PGAUNW, id, NULL, isjson);
+		return;
+	}
+#endif
+
+#ifdef USE_USBUTILS
+	if (cgpu->usbinfo.nodev) {
+		message(io_data, MSG_PGAUSBNODEV, id, NULL, isjson);
+		return;
+	}
+#endif
+
+	for (i = 0; i < mining_threads; i++) {
+		thr = get_thread(i);
+		pga = thr->cgpu->cgminer_id;
+		if (pga == dev) {
+			cgpu->deven = DEV_ENABLED;
+			applog(LOG_DEBUG, "API: Pushing sem post to thread %d", thr->id);
+			cgsem_post(&thr->sem);
+		}
+	}
+
+	message(io_data, MSG_PGAENA, id, NULL, isjson);
+}
+
+static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	int numpga = numpgas();
+	int id;
+
+	if (numpga == 0) {
+		message(io_data, MSG_PGANON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= numpga) {
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	int dev = pgadevice(id);
+	if (dev < 0) { // Should never happen
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	cgpu = get_devices(dev);
+
+	applog(LOG_DEBUG, "API: request to pgadisable pgaid %d device %d %s%u",
+			id, dev, cgpu->drv->name, cgpu->device_id);
+
+	if (cgpu->deven == DEV_DISABLED) {
+		message(io_data, MSG_PGALRDIS, id, NULL, isjson);
+		return;
+	}
+
+	cgpu->deven = DEV_DISABLED;
+
+	message(io_data, MSG_PGADIS, id, NULL, isjson);
+}
+
+static void pgaidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	struct device_drv *drv;
+	int numpga = numpgas();
+	int id;
+
+	if (numpga == 0) {
+		message(io_data, MSG_PGANON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= numpga) {
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	int dev = pgadevice(id);
+	if (dev < 0) { // Should never happen
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	cgpu = get_devices(dev);
+	drv = cgpu->drv;
+
+	if (!drv->identify_device)
+		message(io_data, MSG_PGANOID, id, NULL, isjson);
+	else {
+		drv->identify_device(cgpu);
+		message(io_data, MSG_PGAIDENT, id, NULL, isjson);
+	}
+}
+#endif
+
+static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	bool io_open = false;
+	char *status, *lp;
+	int i;
+	double sdiff0 = 0.0;
+
+	if (total_pools == 0) {
+		message(io_data, MSG_NOPOOL, 0, NULL, isjson);
+		return;
+	}
+
+	message(io_data, MSG_POOL, 0, NULL, isjson);
+
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_POOLS);
+
+	for (i = 0; i < total_pools; i++) {
+		struct pool *pool = pools[i];
+
+		if (pool->removed)
+			continue;
+
+		switch (pool->enabled) {
+			case POOL_DISABLED:
+				status = (char *)DISABLED;
+				break;
+			case POOL_REJECTING:
+				status = (char *)REJECTING;
+				break;
+			case POOL_ENABLED:
+				if (pool->idle)
+					status = (char *)DEAD;
+				else
+					status = (char *)ALIVE;
+				break;
+			default:
+				status = (char *)UNKNOWN;
+				break;
+		}
+
+		if (pool->hdr_path)
+			lp = (char *)YES;
+		else
+			lp = (char *)NO;
+
+		root = api_add_int(root, "POOL", &i, false);
+		root = api_add_escape(root, "URL", pool->rpc_url, false);
+		root = api_add_string(root, "Status", status, false);
+		root = api_add_int(root, "Priority", &(pool->prio), false);
+		root = api_add_int(root, "Quota", &pool->quota, false);
+		root = api_add_string(root, "Long Poll", lp, false);
+		root = api_add_uint(root, "Getworks", &(pool->getwork_requested), false);
+		root = api_add_int64(root, "Accepted", &(pool->accepted), false);
+		root = api_add_int64(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);
+		root = api_add_uint(root, "Remote Failures", &(pool->remotefail_occasions), false);
+		root = api_add_escape(root, "User", pool->rpc_user, false);
+		root = api_add_time(root, "Last Share Time", &(pool->last_share_time), false);
+		root = api_add_int64(root, "Diff1 Shares", &(pool->diff1), false);
+		if (pool->rpc_proxy) {
+			root = api_add_const(root, "Proxy Type", proxytype(pool->rpc_proxytype), false);
+			root = api_add_escape(root, "Proxy", pool->rpc_proxy, false);
+		} else {
+			root = api_add_const(root, "Proxy Type", BLANK, false);
+			root = api_add_const(root, "Proxy", BLANK, false);
+		}
+		root = api_add_diff(root, "Difficulty Accepted", &(pool->diff_accepted), false);
+		root = api_add_diff(root, "Difficulty Rejected", &(pool->diff_rejected), false);
+		root = api_add_diff(root, "Difficulty Stale", &(pool->diff_stale), false);
+		root = api_add_diff(root, "Last Share Difficulty", &(pool->last_share_diff), false);
+		root = api_add_diff(root, "Work Difficulty", &(pool->cgminer_pool_stats.last_diff), false);
+		root = api_add_bool(root, "Has Stratum", &(pool->has_stratum), false);
+		root = api_add_bool(root, "Stratum Active", &(pool->stratum_active), false);
+		if (pool->stratum_active) {
+			root = api_add_escape(root, "Stratum URL", pool->stratum_url, false);
+			root = api_add_diff(root, "Stratum Difficulty", &(pool->sdiff), false);
+		} else {
+			root = api_add_const(root, "Stratum URL", BLANK, false);
+			root = api_add_diff(root, "Stratum Difficulty", &(sdiff0), false);
+		}
+		root = api_add_bool(root, "Has Vmask", &(pool->vmask), false);
+		root = api_add_bool(root, "Has GBT", &(pool->has_gbt), false);
+		root = api_add_uint64(root, "Best Share", &(pool->best_diff), true);
+		double rejp = (pool->diff_accepted + pool->diff_rejected + pool->diff_stale) ?
+				(double)(pool->diff_rejected) / (double)(pool->diff_accepted + pool->diff_rejected + pool->diff_stale) : 0;
+		root = api_add_percent(root, "Pool Rejected%", &rejp, false);
+		double stalep = (pool->diff_accepted + pool->diff_rejected + pool->diff_stale) ?
+				(double)(pool->diff_stale) / (double)(pool->diff_accepted + pool->diff_rejected + pool->diff_stale) : 0;
+		root = api_add_percent(root, "Pool Stale%", &stalep, false);
+		root = api_add_uint64(root, "Bad Work", &(pool->bad_work), true);
+		root = api_add_uint32(root, "Current Block Height", &(pool->current_height), true);
+		uint32_t nversion = (uint32_t)strtoul(pool->bbversion, NULL, 16);
+		root = api_add_uint32(root, "Current Block Version", &nversion, true);
+
+		root = print_data(io_data, root, isjson, isjson && (i > 0));
+	}
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void summary(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	bool io_open;
+	double utility, mhs, work_utility;
+
+	message(io_data, MSG_SUMM, 0, NULL, isjson);
+	io_open = io_add(io_data, isjson ? COMSTR JSON_SUMMARY : _SUMMARY COMSTR);
+
+	// stop hashmeter() changing some while copying
+	mutex_lock(&hash_lock);
+
+	utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60;
+	mhs = total_mhashes_done / total_secs;
+	work_utility = total_diff1 / ( total_secs ? total_secs : 1 ) * 60;
+
+	root = api_add_elapsed(root, "Elapsed", &(total_secs), true);
+	root = api_add_mhs(root, "MHS av", &(mhs), false);
+	char mhsname[27];
+	snprintf(mhsname, sizeof(mhsname),  "MHS %ds", opt_log_interval);
+	root = api_add_mhs(root, mhsname, &(total_rolling), false);
+	root = api_add_mhs(root, "MHS 1m", &rolling1, false);
+	root = api_add_mhs(root, "MHS 5m", &rolling5, false);
+	root = api_add_mhs(root, "MHS 15m", &rolling15, false);
+	root = api_add_uint(root, "Found Blocks", &(found_blocks), true);
+	root = api_add_int64(root, "Getworks", &(total_getworks), true);
+	root = api_add_int64(root, "Accepted", &(total_accepted), true);
+	root = api_add_int64(root, "Rejected", &(total_rejected), true);
+	root = api_add_int(root, "Hardware Errors", &(hw_errors), true);
+	root = api_add_utility(root, "Utility", &(utility), false);
+	root = api_add_int64(root, "Discarded", &(total_discarded), true);
+	root = api_add_int64(root, "Stale", &(total_stale), true);
+	root = api_add_uint(root, "Get Failures", &(total_go), true);
+	root = api_add_uint(root, "Local Work", &(local_work), true);
+	root = api_add_uint(root, "Remote Failures", &(total_ro), true);
+	root = api_add_uint(root, "Network Blocks", &(new_blocks), true);
+	root = api_add_mhtotal(root, "Total MH", &(total_mhashes_done), true);
+	root = api_add_utility(root, "Work Utility", &(work_utility), false);
+	root = api_add_diff(root, "Difficulty Accepted", &(total_diff_accepted), true);
+	root = api_add_diff(root, "Difficulty Rejected", &(total_diff_rejected), true);
+	root = api_add_diff(root, "Difficulty Stale", &(total_diff_stale), true);
+	root = api_add_uint64(root, "Best Share", &(best_diff), true);
+	double hwp = (hw_errors + total_diff1) ?
+			(double)(hw_errors) / (double)(hw_errors + total_diff1) : 0;
+	root = api_add_percent(root, "Device Hardware%", &hwp, false);
+	double rejp = total_diff1 ?
+			(double)(total_diff_rejected) / (double)(total_diff1) : 0;
+	root = api_add_percent(root, "Device Rejected%", &rejp, false);
+	double prejp = (total_diff_accepted + total_diff_rejected + total_diff_stale) ?
+			(double)(total_diff_rejected) / (double)(total_diff_accepted + total_diff_rejected + total_diff_stale) : 0;
+	root = api_add_percent(root, "Pool Rejected%", &prejp, false);
+	double stalep = (total_diff_accepted + total_diff_rejected + total_diff_stale) ?
+			(double)(total_diff_stale) / (double)(total_diff_accepted + total_diff_rejected + total_diff_stale) : 0;
+	root = api_add_percent(root, "Pool Stale%", &stalep, false);
+	root = api_add_time(root, "Last getwork", &last_getwork, false);
+
+	mutex_unlock(&hash_lock);
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void pgacount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	bool io_open;
+	int count = 0;
+
+#ifdef HAVE_AN_FPGA
+	count = numpgas();
+#endif
+
+	message(io_data, MSG_NUMPGA, 0, NULL, isjson);
+	io_open = io_add(io_data, isjson ? COMSTR JSON_PGAS : _PGAS COMSTR);
+
+	root = api_add_int(root, "Count", &count, false);
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void switchpool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct pool *pool;
+	int id;
+
+	if (total_pools == 0) {
+		message(io_data, MSG_NOPOOL, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISPID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	cg_rlock(&control_lock);
+	if (id < 0 || id >= total_pools) {
+		cg_runlock(&control_lock);
+		message(io_data, MSG_INVPID, id, NULL, isjson);
+		return;
+	}
+
+	pool = pools[id];
+	pool->enabled = POOL_ENABLED;
+	cg_runlock(&control_lock);
+	switch_pools(pool);
+
+	message(io_data, MSG_SWITCHP, id, NULL, isjson);
+}
+
+static void copyadvanceafter(char ch, char **param, char **buf)
+{
+#define src_p (*param)
+#define dst_b (*buf)
+
+	while (*src_p && *src_p != ch) {
+		if (*src_p == '\\' && *(src_p+1) != '\0')
+			src_p++;
+
+		*(dst_b++) = *(src_p++);
+	}
+	if (*src_p)
+		src_p++;
+
+	*(dst_b++) = '\0';
+}
+
+static bool pooldetails(char *param, char **url, char **user, char **pass)
+{
+	char *ptr, *buf;
+
+	ptr = buf = cgmalloc(strlen(param)+1);
+
+	*url = buf;
+
+	// copy url
+	copyadvanceafter(',', &param, &buf);
+
+	if (!(*param)) // missing user
+		goto exitsama;
+
+	*user = buf;
+
+	// copy user
+	copyadvanceafter(',', &param, &buf);
+
+	if (!*param) // missing pass
+		goto exitsama;
+
+	*pass = buf;
+
+	// copy pass
+	copyadvanceafter(',', &param, &buf);
+
+	return true;
+
+exitsama:
+	free(ptr);
+	return false;
+}
+
+static void addpool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	char *url, *user, *pass;
+	struct pool *pool;
+	char *ptr;
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISPDP, 0, NULL, isjson);
+		return;
+	}
+
+	if (!pooldetails(param, &url, &user, &pass)) {
+		ptr = escape_string(param, isjson);
+		message(io_data, MSG_INVPDP, 0, ptr, isjson);
+		if (ptr != param)
+			free(ptr);
+		ptr = NULL;
+		return;
+	}
+
+	pool = add_pool();
+	detect_stratum(pool, url);
+	add_pool_details(pool, true, url, user, pass);
+
+	ptr = escape_string(url, isjson);
+	message(io_data, MSG_ADDPOOL, pool->pool_no, ptr, isjson);
+	if (ptr != url)
+		free(ptr);
+	ptr = NULL;
+}
+
+static void enablepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct pool *pool;
+	int id;
+
+	if (total_pools == 0) {
+		message(io_data, MSG_NOPOOL, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISPID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= total_pools) {
+		message(io_data, MSG_INVPID, id, NULL, isjson);
+		return;
+	}
+
+	pool = pools[id];
+	if (pool->enabled == POOL_ENABLED) {
+		message(io_data, MSG_ALRENAP, id, NULL, isjson);
+		return;
+	}
+
+	pool->enabled = POOL_ENABLED;
+	if (pool->prio < current_pool()->prio)
+		switch_pools(pool);
+
+	message(io_data, MSG_ENAPOOL, id, NULL, isjson);
+}
+
+static void poolpriority(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	char *ptr, *next;
+	int i, pr, prio = 0;
+
+	// TODO: all cgminer code needs a mutex added everywhere for change
+	//	access to total_pools and also parts of the pools[] array,
+	//	just copying total_pools here wont solve that
+
+	if (total_pools == 0) {
+		message(io_data, MSG_NOPOOL, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISPID, 0, NULL, isjson);
+		return;
+	}
+
+	bool pools_changed[total_pools];
+	int new_prio[total_pools];
+	for (i = 0; i < total_pools; ++i)
+		pools_changed[i] = false;
+
+	next = param;
+	while (next && *next) {
+		ptr = next;
+		next = strchr(ptr, ',');
+		if (next)
+			*(next++) = '\0';
+
+		i = atoi(ptr);
+		if (i < 0 || i >= total_pools) {
+			message(io_data, MSG_INVPID, i, NULL, isjson);
+			return;
+		}
+
+		if (pools_changed[i]) {
+			message(io_data, MSG_DUPPID, i, NULL, isjson);
+			return;
+		}
+
+		pools_changed[i] = true;
+		new_prio[i] = prio++;
+	}
+
+	// Only change them if no errors
+	for (i = 0; i < total_pools; i++) {
+		if (pools_changed[i])
+			pools[i]->prio = new_prio[i];
+	}
+
+	// In priority order, cycle through the unchanged pools and append them
+	for (pr = 0; pr < total_pools; pr++)
+		for (i = 0; i < total_pools; i++) {
+			if (!pools_changed[i] && pools[i]->prio == pr) {
+				pools[i]->prio = prio++;
+				pools_changed[i] = true;
+				break;
+			}
+		}
+
+	if (current_pool()->prio)
+		switch_pools(NULL);
+
+	message(io_data, MSG_POOLPRIO, 0, NULL, isjson);
+}
+
+static void poolquota(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct pool *pool;
+	int quota, id;
+	char *comma;
+
+	if (total_pools == 0) {
+		message(io_data, MSG_NOPOOL, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISPID, 0, NULL, isjson);
+		return;
+	}
+
+	comma = strchr(param, ',');
+	if (!comma) {
+		message(io_data, MSG_CONVAL, 0, param, isjson);
+		return;
+	}
+
+	*(comma++) = '\0';
+
+	id = atoi(param);
+	if (id < 0 || id >= total_pools) {
+		message(io_data, MSG_INVPID, id, NULL, isjson);
+		return;
+	}
+	pool = pools[id];
+
+	quota = atoi(comma);
+	if (quota < 0) {
+		message(io_data, MSG_INVNEG, quota, pool->rpc_url, isjson);
+		return;
+	}
+
+	pool->quota = quota;
+	adjust_quota_gcd();
+	message(io_data, MSG_SETQUOTA, quota, pool->rpc_url, isjson);
+}
+
+static void disablepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct pool *pool;
+	int id;
+
+	if (total_pools == 0) {
+		message(io_data, MSG_NOPOOL, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISPID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= total_pools) {
+		message(io_data, MSG_INVPID, id, NULL, isjson);
+		return;
+	}
+
+	pool = pools[id];
+	if (pool->enabled == POOL_DISABLED) {
+		message(io_data, MSG_ALRDISP, id, NULL, isjson);
+		return;
+	}
+
+	if (enabled_pools <= 1) {
+		message(io_data, MSG_DISLASTP, id, NULL, isjson);
+		return;
+	}
+
+	pool->enabled = POOL_DISABLED;
+	if (pool == current_pool())
+		switch_pools(NULL);
+
+	message(io_data, MSG_DISPOOL, id, NULL, isjson);
+}
+
+static void removepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct pool *pool;
+	char *rpc_url;
+	bool dofree = false;
+	int id;
+
+	if (total_pools == 0) {
+		message(io_data, MSG_NOPOOL, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISPID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= total_pools) {
+		message(io_data, MSG_INVPID, id, NULL, isjson);
+		return;
+	}
+
+	if (total_pools <= 1) {
+		message(io_data, MSG_REMLASTP, id, NULL, isjson);
+		return;
+	}
+
+	pool = pools[id];
+	if (pool == current_pool())
+		switch_pools(NULL);
+
+	if (pool == current_pool()) {
+		message(io_data, MSG_ACTPOOL, id, NULL, isjson);
+		return;
+	}
+
+	pool->enabled = POOL_DISABLED;
+	rpc_url = escape_string(pool->rpc_url, isjson);
+	if (rpc_url != pool->rpc_url)
+		dofree = true;
+
+	remove_pool(pool);
+
+	message(io_data, MSG_REMPOOL, id, rpc_url, isjson);
+
+	if (dofree)
+		free(rpc_url);
+	rpc_url = NULL;
+}
+
+void doquit(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	if (isjson)
+		io_put(io_data, JSON_ACTION JSON_BYE);
+	else
+		io_put(io_data, _BYE);
+
+	bye = true;
+	do_a_quit = true;
+}
+
+void dorestart(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	if (isjson)
+		io_put(io_data, JSON_ACTION JSON_RESTART);
+	else
+		io_put(io_data, _RESTART);
+
+	bye = true;
+	do_a_restart = true;
+}
+
+void privileged(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	message(io_data, MSG_ACCOK, 0, NULL, isjson);
+}
+
+void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	char *reason;
+
+	if (cgpu->device_last_not_well == 0)
+		reason = REASON_NONE;
+	else
+		switch(cgpu->device_not_well_reason) {
+			case REASON_THREAD_FAIL_INIT:
+				reason = REASON_THREAD_FAIL_INIT_STR;
+				break;
+			case REASON_THREAD_ZERO_HASH:
+				reason = REASON_THREAD_ZERO_HASH_STR;
+				break;
+			case REASON_THREAD_FAIL_QUEUE:
+				reason = REASON_THREAD_FAIL_QUEUE_STR;
+				break;
+			case REASON_DEV_SICK_IDLE_60:
+				reason = REASON_DEV_SICK_IDLE_60_STR;
+				break;
+			case REASON_DEV_DEAD_IDLE_600:
+				reason = REASON_DEV_DEAD_IDLE_600_STR;
+				break;
+			case REASON_DEV_NOSTART:
+				reason = REASON_DEV_NOSTART_STR;
+				break;
+			case REASON_DEV_OVER_HEAT:
+				reason = REASON_DEV_OVER_HEAT_STR;
+				break;
+			case REASON_DEV_THERMAL_CUTOFF:
+				reason = REASON_DEV_THERMAL_CUTOFF_STR;
+				break;
+			case REASON_DEV_COMMS_ERROR:
+				reason = REASON_DEV_COMMS_ERROR_STR;
+				break;
+			default:
+				reason = REASON_UNKNOWN_STR;
+				break;
+		}
+
+	// ALL counters (and only counters) must start the name with a '*'
+	// Simplifies future external support for identifying new counters
+	root = api_add_int(root, "NOTIFY", &device, false);
+	root = api_add_string(root, "Name", cgpu->drv->name, false);
+	root = api_add_int(root, "ID", &(cgpu->device_id), false);
+	root = api_add_time(root, "Last Well", &(cgpu->device_last_well), false);
+	root = api_add_time(root, "Last Not Well", &(cgpu->device_last_not_well), false);
+	root = api_add_string(root, "Reason Not Well", reason, false);
+	root = api_add_int(root, "*Thread Fail Init", &(cgpu->thread_fail_init_count), false);
+	root = api_add_int(root, "*Thread Zero Hash", &(cgpu->thread_zero_hash_count), false);
+	root = api_add_int(root, "*Thread Fail Queue", &(cgpu->thread_fail_queue_count), false);
+	root = api_add_int(root, "*Dev Sick Idle 60s", &(cgpu->dev_sick_idle_60_count), false);
+	root = api_add_int(root, "*Dev Dead Idle 600s", &(cgpu->dev_dead_idle_600_count), false);
+	root = api_add_int(root, "*Dev Nostart", &(cgpu->dev_nostart_count), false);
+	root = api_add_int(root, "*Dev Over Heat", &(cgpu->dev_over_heat_count), false);
+	root = api_add_int(root, "*Dev Thermal Cutoff", &(cgpu->dev_thermal_cutoff_count), false);
+	root = api_add_int(root, "*Dev Comms Error", &(cgpu->dev_comms_error_count), false);
+	root = api_add_int(root, "*Dev Throttle", &(cgpu->dev_throttle_count), false);
+
+	root = print_data(io_data, root, isjson, isjson && (device > 0));
+}
+
+static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, char group)
+{
+	struct cgpu_info *cgpu;
+	bool io_open = false;
+	int i;
+
+	if (total_devices == 0) {
+		message(io_data, MSG_NODEVS, 0, NULL, isjson);
+		return;
+	}
+
+	message(io_data, MSG_NOTIFY, 0, NULL, isjson);
+
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_NOTIFY);
+
+	for (i = 0; i < total_devices; i++) {
+		cgpu = get_devices(i);
+		notifystatus(io_data, i, cgpu, isjson, group);
+	}
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void devdetails(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	bool io_open = false;
+	struct cgpu_info *cgpu;
+	int i;
+
+	if (total_devices == 0) {
+		message(io_data, MSG_NODEVS, 0, NULL, isjson);
+		return;
+	}
+
+	message(io_data, MSG_DEVDETAILS, 0, NULL, isjson);
+
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_DEVDETAILS);
+
+	for (i = 0; i < total_devices; i++) {
+		cgpu = get_devices(i);
+
+		root = api_add_int(root, "DEVDETAILS", &i, false);
+		root = api_add_string(root, "Name", cgpu->drv->name, false);
+		root = api_add_int(root, "ID", &(cgpu->device_id), false);
+		root = api_add_string(root, "Driver", cgpu->drv->dname, false);
+		root = api_add_const(root, "Kernel", cgpu->kname ? : BLANK, false);
+		root = api_add_const(root, "Model", cgpu->name ? : BLANK, false);
+		root = api_add_const(root, "Device Path", cgpu->device_path ? : BLANK, false);
+
+		root = print_data(io_data, root, isjson, isjson && (i > 0));
+	}
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+void dosave(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	char filename[PATH_MAX];
+	FILE *fcfg;
+	char *ptr;
+
+	if (param == NULL || *param == '\0') {
+		default_save_file(filename);
+		param = filename;
+	}
+
+	fcfg = fopen(param, "w");
+	if (!fcfg) {
+		ptr = escape_string(param, isjson);
+		message(io_data, MSG_BADFN, 0, ptr, isjson);
+		if (ptr != param)
+			free(ptr);
+		ptr = NULL;
+		return;
+	}
+
+	write_config(fcfg);
+	fclose(fcfg);
+
+	ptr = escape_string(param, isjson);
+	message(io_data, MSG_SAVED, 0, ptr, isjson);
+	if (ptr != param)
+		free(ptr);
+	ptr = NULL;
+}
+
+static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_stats *stats, struct cgminer_pool_stats *pool_stats, struct api_data *extra, struct cgpu_info *cgpu, bool isjson)
+{
+	struct api_data *root = NULL;
+
+	root = api_add_int(root, "STATS", &i, false);
+	root = api_add_string(root, "ID", id, false);
+	root = api_add_elapsed(root, "Elapsed", &(total_secs), false);
+	root = api_add_uint32(root, "Calls", &(stats->getwork_calls), false);
+	root = api_add_timeval(root, "Wait", &(stats->getwork_wait), false);
+	root = api_add_timeval(root, "Max", &(stats->getwork_wait_max), false);
+	root = api_add_timeval(root, "Min", &(stats->getwork_wait_min), false);
+
+	if (pool_stats) {
+		root = api_add_uint32(root, "Pool Calls", &(pool_stats->getwork_calls), false);
+		root = api_add_uint32(root, "Pool Attempts", &(pool_stats->getwork_attempts), false);
+		root = api_add_timeval(root, "Pool Wait", &(pool_stats->getwork_wait), false);
+		root = api_add_timeval(root, "Pool Max", &(pool_stats->getwork_wait_max), false);
+		root = api_add_timeval(root, "Pool Min", &(pool_stats->getwork_wait_min), false);
+		root = api_add_double(root, "Pool Av", &(pool_stats->getwork_wait_rolling), false);
+		root = api_add_bool(root, "Work Had Roll Time", &(pool_stats->hadrolltime), false);
+		root = api_add_bool(root, "Work Can Roll", &(pool_stats->canroll), false);
+		root = api_add_bool(root, "Work Had Expire", &(pool_stats->hadexpire), false);
+		root = api_add_uint32(root, "Work Roll Time", &(pool_stats->rolltime), false);
+		root = api_add_diff(root, "Work Diff", &(pool_stats->last_diff), false);
+		root = api_add_diff(root, "Min Diff", &(pool_stats->min_diff), false);
+		root = api_add_diff(root, "Max Diff", &(pool_stats->max_diff), false);
+		root = api_add_uint32(root, "Min Diff Count", &(pool_stats->min_diff_count), false);
+		root = api_add_uint32(root, "Max Diff Count", &(pool_stats->max_diff_count), false);
+		root = api_add_uint64(root, "Times Sent", &(pool_stats->times_sent), false);
+		root = api_add_uint64(root, "Bytes Sent", &(pool_stats->bytes_sent), false);
+		root = api_add_uint64(root, "Times Recv", &(pool_stats->times_received), false);
+		root = api_add_uint64(root, "Bytes Recv", &(pool_stats->bytes_received), false);
+		root = api_add_uint64(root, "Net Bytes Sent", &(pool_stats->net_bytes_sent), false);
+		root = api_add_uint64(root, "Net Bytes Recv", &(pool_stats->net_bytes_received), false);
+	}
+
+	if (extra)
+		root = api_add_extra(root, extra);
+
+	if (cgpu) {
+#ifdef USE_USBUTILS
+		char details[256];
+
+		if (cgpu->usbinfo.pipe_count)
+			snprintf(details, sizeof(details),
+				 "%"PRIu64" %"PRIu64"/%"PRIu64"/%"PRIu64" %lu",
+				 cgpu->usbinfo.pipe_count,
+				 cgpu->usbinfo.clear_err_count,
+				 cgpu->usbinfo.retry_err_count,
+				 cgpu->usbinfo.clear_fail_count,
+				 (unsigned long)(cgpu->usbinfo.last_pipe));
+		else
+			strcpy(details, "0");
+
+		root = api_add_string(root, "USB Pipe", details, true);
+
+		snprintf(details, sizeof(details),
+			 "r%"PRIu64" %.6f w%"PRIu64" %.6f",
+			 cgpu->usbinfo.read_delay_count,
+			 cgpu->usbinfo.total_read_delay,
+			 cgpu->usbinfo.write_delay_count,
+			 cgpu->usbinfo.total_write_delay);
+
+		root = api_add_string(root, "USB Delay", details, true);
+
+		if (cgpu->usbinfo.usb_tmo[0].count == 0 &&
+			cgpu->usbinfo.usb_tmo[1].count == 0 &&
+			cgpu->usbinfo.usb_tmo[2].count == 0) {
+				snprintf(details, sizeof(details),
+					 "%"PRIu64" 0", cgpu->usbinfo.tmo_count);
+		} else {
+			snprintf(details, sizeof(details),
+				 "%"PRIu64" %d=%d/%d/%d/%"PRIu64"/%"PRIu64
+				 " %d=%d/%d/%d/%"PRIu64"/%"PRIu64
+				 " %d=%d/%d/%d/%"PRIu64"/%"PRIu64" ",
+				 cgpu->usbinfo.tmo_count,
+				 USB_TMO_0, cgpu->usbinfo.usb_tmo[0].count,
+				 cgpu->usbinfo.usb_tmo[0].min_tmo,
+				 cgpu->usbinfo.usb_tmo[0].max_tmo,
+				 cgpu->usbinfo.usb_tmo[0].total_over,
+				 cgpu->usbinfo.usb_tmo[0].total_tmo,
+				 USB_TMO_1, cgpu->usbinfo.usb_tmo[1].count,
+				 cgpu->usbinfo.usb_tmo[1].min_tmo,
+				 cgpu->usbinfo.usb_tmo[1].max_tmo,
+				 cgpu->usbinfo.usb_tmo[1].total_over,
+				 cgpu->usbinfo.usb_tmo[1].total_tmo,
+				 USB_TMO_2, cgpu->usbinfo.usb_tmo[2].count,
+				 cgpu->usbinfo.usb_tmo[2].min_tmo,
+				 cgpu->usbinfo.usb_tmo[2].max_tmo,
+				 cgpu->usbinfo.usb_tmo[2].total_over,
+				 cgpu->usbinfo.usb_tmo[2].total_tmo);
+		}
+
+		root = api_add_string(root, "USB tmo", details, true);
+#endif
+	}
+
+	root = print_data(io_data, root, isjson, isjson && (i > 0));
+
+	return ++i;
+}
+
+static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	bool io_open = false;
+	struct api_data *extra;
+	char id[20];
+	int i, j;
+
+	message(io_data, MSG_MINESTATS, 0, NULL, isjson);
+
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_MINESTATS);
+
+	i = 0;
+	for (j = 0; j < total_devices; j++) {
+		cgpu = get_devices(j);
+
+		if (cgpu && cgpu->drv) {
+			if (cgpu->drv->get_api_stats)
+				extra = cgpu->drv->get_api_stats(cgpu);
+			else
+				extra = NULL;
+
+			snprintf(id, sizeof(id), "%s%d", cgpu->drv->name, cgpu->device_id);
+			i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, cgpu, isjson);
+		}
+	}
+
+	for (j = 0; j < total_pools; j++) {
+		struct pool *pool = pools[j];
+
+		snprintf(id, sizeof(id), "POOL%d", j);
+		i = itemstats(io_data, i, id, &(pool->cgminer_stats), &(pool->cgminer_pool_stats), NULL, NULL, isjson);
+	}
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void minerdebug(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	bool io_open = false;
+	struct api_data *extra;
+	char id[20];
+	int i, j;
+
+	message(io_data, MSG_MINEDEBUG, 0, NULL, isjson);
+
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_MINESTATS);
+
+	i = 0;
+	for (j = 0; j < total_devices; j++) {
+		cgpu = get_devices(j);
+
+		if (cgpu && cgpu->drv) {
+			if (cgpu->drv->get_api_debug)
+				extra = cgpu->drv->get_api_debug(cgpu);
+			else
+				extra = NULL;
+
+			snprintf(id, sizeof(id), "%s%d", cgpu->drv->name, cgpu->device_id);
+			i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, cgpu, isjson);
+		}
+	}
+
+	for (j = 0; j < total_pools; j++) {
+		struct pool *pool = pools[j];
+
+		snprintf(id, sizeof(id), "POOL%d", j);
+		i = itemstats(io_data, i, id, &(pool->cgminer_stats), &(pool->cgminer_pool_stats), NULL, NULL, isjson);
+	}
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void minerestats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	bool io_open = false;
+	struct api_data *extra;
+	char id[20];
+	int i, j;
+#ifdef USE_USBUTILS
+	time_t howoldsec = 0;
+
+	if (param && *param)
+		howoldsec = (time_t)atoi(param);
+#endif
+
+	message(io_data, MSG_MINESTATS, 0, NULL, isjson);
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_MINESTATS);
+
+	i = 0;
+	for (j = 0; j < total_devices; j++) {
+		cgpu = get_devices(j);
+		if (!cgpu)
+			continue;
+#ifdef USE_USBUTILS
+		if (cgpu->blacklisted)
+			continue;
+		if (cgpu->usbinfo.nodev) {
+			if (howoldsec <= 0)
+				continue;
+			if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec)
+				continue;
+		}
+#endif
+		if (cgpu->drv) {
+			if (cgpu->drv->get_api_stats)
+				extra = cgpu->drv->get_api_stats(cgpu);
+			else
+				extra = NULL;
+
+			snprintf(id, sizeof(id), "%s%d", cgpu->drv->name, cgpu->device_id);
+			i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, cgpu, isjson);
+		}
+	}
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void failoveronly(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	message(io_data, MSG_DEPRECATED, 0, param, isjson);
+}
+
+static void minecoin(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	bool io_open;
+
+	message(io_data, MSG_MINECOIN, 0, NULL, isjson);
+	io_open = io_add(io_data, isjson ? COMSTR JSON_MINECOIN : _MINECOIN COMSTR);
+
+	root = api_add_const(root, "Hash Method", SHA256STR, false);
+
+	cg_rlock(&ch_lock);
+	root = api_add_timeval(root, "Current Block Time", &block_timeval, true);
+	root = api_add_string(root, "Current Block Hash", current_hash, true);
+	cg_runlock(&ch_lock);
+
+	root = api_add_bool(root, "LP", &have_longpoll, false);
+	root = api_add_diff(root, "Network Difficulty", &current_diff, true);
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void debugstate(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	bool io_open;
+
+	if (param == NULL)
+		param = (char *)BLANK;
+	else
+		*param = tolower(*param);
+
+	switch(*param) {
+	case 's':
+		opt_realquiet = true;
+		break;
+	case 'q':
+		opt_quiet ^= true;
+		break;
+	case 'v':
+		opt_log_output ^= true;
+		if (opt_log_output)
+			opt_quiet = false;
+		break;
+	case 'd':
+		opt_debug ^= true;
+		opt_log_output = opt_debug;
+		if (opt_debug)
+			opt_quiet = false;
+		break;
+	case 'r':
+		opt_protocol ^= true;
+		if (opt_protocol)
+			opt_quiet = false;
+		break;
+	case 'p':
+		want_per_device_stats ^= true;
+		opt_log_output = want_per_device_stats;
+		break;
+	case 'n':
+		opt_log_output = false;
+		opt_debug = false;
+		opt_quiet = false;
+		opt_protocol = false;
+		want_per_device_stats = false;
+		opt_worktime = false;
+		break;
+	case 'w':
+		opt_worktime ^= true;
+		break;
+#ifdef _MEMORY_DEBUG
+	case 'y':
+		cgmemspeedup();
+		break;
+	case 'z':
+		cgmemrpt();
+		break;
+#endif
+	default:
+		// anything else just reports the settings
+		break;
+	}
+
+	message(io_data, MSG_DEBUGSET, 0, NULL, isjson);
+	io_open = io_add(io_data, isjson ? COMSTR JSON_DEBUGSET : _DEBUGSET COMSTR);
+
+	root = api_add_bool(root, "Silent", &opt_realquiet, false);
+	root = api_add_bool(root, "Quiet", &opt_quiet, false);
+	root = api_add_bool(root, "Verbose", &opt_log_output, false);
+	root = api_add_bool(root, "Debug", &opt_debug, false);
+	root = api_add_bool(root, "RPCProto", &opt_protocol, false);
+	root = api_add_bool(root, "PerDevice", &want_per_device_stats, false);
+	root = api_add_bool(root, "WorkTime", &opt_worktime, false);
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void setconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	if (!strcasecmp(param, "queue") || ! strcasecmp(param, "scantime") || !strcasecmp(param, "expiry"))
+		message(io_data, MSG_DEPRECATED, 0, param, isjson);
+
+	message(io_data, MSG_UNKCON, 0, param, isjson);
+}
+
+static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+
+#ifdef USE_USBUTILS
+	bool io_open = false;
+	int count = 0;
+
+	root = api_usb_stats(&count);
+#endif
+
+	if (!root) {
+		message(io_data, MSG_NOUSTA, 0, NULL, isjson);
+		return;
+	}
+
+#ifdef USE_USBUTILS
+	message(io_data, MSG_USBSTA, 0, NULL, isjson);
+
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_USBSTATS);
+
+	root = print_data(io_data, root, isjson, false);
+
+	while (42) {
+		root = api_usb_stats(&count);
+		if (!root)
+			break;
+
+		root = print_data(io_data, root, isjson, isjson);
+	}
+
+	if (isjson && io_open)
+		io_close(io_data);
+#endif
+}
+
+#ifdef HAVE_AN_FPGA
+static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	struct device_drv *drv;
+	char buf[TMPBUFSIZ];
+	int numpga = numpgas();
+
+	if (numpga == 0) {
+		message(io_data, MSG_PGANON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	char *opt = strchr(param, ',');
+	if (opt)
+		*(opt++) = '\0';
+	if (!opt || !*opt) {
+		message(io_data, MSG_MISPGAOPT, 0, NULL, isjson);
+		return;
+	}
+
+	int id = atoi(param);
+	if (id < 0 || id >= numpga) {
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	int dev = pgadevice(id);
+	if (dev < 0) { // Should never happen
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	cgpu = get_devices(dev);
+	drv = cgpu->drv;
+
+	char *set = strchr(opt, ',');
+	if (set)
+		*(set++) = '\0';
+
+	if (!drv->set_device)
+		message(io_data, MSG_PGANOSET, id, NULL, isjson);
+	else {
+		char *ret = drv->set_device(cgpu, opt, set, buf, sizeof(buf));
+		if (ret) {
+			if (strcasecmp(opt, "help") == 0)
+				message(io_data, MSG_PGAHELP, id, ret, isjson);
+			else
+				message(io_data, MSG_PGASETERR, id, ret, isjson);
+		} else
+			message(io_data, MSG_PGASETOK, id, NULL, isjson);
+	}
+}
+#endif
+
+static void dozero(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_ZERMIS, 0, NULL, isjson);
+		return;
+	}
+
+	char *sum = strchr(param, ',');
+	if (sum)
+		*(sum++) = '\0';
+	if (!sum || !*sum) {
+		message(io_data, MSG_MISBOOL, 0, NULL, isjson);
+		return;
+	}
+
+	bool all = false;
+	bool bs = false;
+	if (strcasecmp(param, "all") == 0)
+		all = true;
+	else if (strcasecmp(param, "bestshare") == 0)
+		bs = true;
+
+	if (all == false && bs == false) {
+		message(io_data, MSG_ZERINV, 0, param, isjson);
+		return;
+	}
+
+	*sum = tolower(*sum);
+	if (*sum != 't' && *sum != 'f') {
+		message(io_data, MSG_INVBOOL, 0, NULL, isjson);
+		return;
+	}
+
+	bool dosum = (*sum == 't');
+	if (dosum)
+		print_summary();
+
+	if (all)
+		zero_stats();
+	if (bs)
+		zero_bestshare();
+
+	if (dosum)
+		message(io_data, MSG_ZERSUM, 0, all ? "All" : "BestShare", isjson);
+	else
+		message(io_data, MSG_ZERNOSUM, 0, all ? "All" : "BestShare", isjson);
+}
+
+static void dohotplug(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+#ifdef USE_USBUTILS
+	int value;
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISHPLG, 0, NULL, isjson);
+		return;
+	}
+
+	value = atoi(param);
+	if (value < 0 || value > 9999) {
+		message(io_data, MSG_INVHPLG, 0, param, isjson);
+		return;
+	}
+
+	hotplug_time = value;
+
+	if (value)
+		message(io_data, MSG_HOTPLUG, value, NULL, isjson);
+	else
+		message(io_data, MSG_DISHPLG, 0, NULL, isjson);
+#else
+	message(io_data, MSG_NOHPLG, 0, NULL, isjson);
+	return;
+#endif
+}
+
+#ifdef HAVE_AN_ASIC
+static void ascdev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	bool io_open = false;
+	int numasc = numascs();
+	int id;
+
+	if (numasc == 0) {
+		message(io_data, MSG_ASCNON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= numasc) {
+		message(io_data, MSG_INVASC, id, NULL, isjson);
+		return;
+	}
+
+	message(io_data, MSG_ASCDEV, id, NULL, isjson);
+
+	if (isjson)
+		io_open = io_add(io_data, COMSTR JSON_ASC);
+
+	ascstatus(io_data, id, isjson, false);
+
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void ascenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	int numasc = numascs();
+	struct thr_info *thr;
+	int asc;
+	int id;
+	int i;
+
+	if (numasc == 0) {
+		message(io_data, MSG_ASCNON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= numasc) {
+		message(io_data, MSG_INVASC, id, NULL, isjson);
+		return;
+	}
+
+	int dev = ascdevice(id);
+	if (dev < 0) { // Should never happen
+		message(io_data, MSG_INVASC, id, NULL, isjson);
+		return;
+	}
+
+	cgpu = get_devices(dev);
+
+	applog(LOG_DEBUG, "API: request to ascenable ascid %d device %d %s%u",
+			id, dev, cgpu->drv->name, cgpu->device_id);
+
+	if (cgpu->deven != DEV_DISABLED) {
+		message(io_data, MSG_ASCLRENA, id, NULL, isjson);
+		return;
+	}
+
+#if 0 /* A DISABLED device wont change status FIXME: should disabling make it WELL? */
+	if (cgpu->status != LIFE_WELL) {
+		message(io_data, MSG_ASCUNW, id, NULL, isjson);
+		return;
+	}
+#endif
+
+#ifdef USE_USBUTILS
+	if (cgpu->usbinfo.nodev) {
+		message(io_data, MSG_ASCUSBNODEV, id, NULL, isjson);
+		return;
+	}
+#endif
+
+	for (i = 0; i < mining_threads; i++) {
+		thr = get_thread(i);
+		asc = thr->cgpu->cgminer_id;
+		if (asc == dev) {
+			cgpu->deven = DEV_ENABLED;
+			applog(LOG_DEBUG, "API: Pushing sem post to thread %d", thr->id);
+			cgsem_post(&thr->sem);
+		}
+	}
+
+	message(io_data, MSG_ASCENA, id, NULL, isjson);
+}
+
+static void ascdisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	int numasc = numascs();
+	int id;
+
+	if (numasc == 0) {
+		message(io_data, MSG_ASCNON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= numasc) {
+		message(io_data, MSG_INVASC, id, NULL, isjson);
+		return;
+	}
+
+	int dev = ascdevice(id);
+	if (dev < 0) { // Should never happen
+		message(io_data, MSG_INVASC, id, NULL, isjson);
+		return;
+	}
+
+	cgpu = get_devices(dev);
+
+	applog(LOG_DEBUG, "API: request to ascdisable ascid %d device %d %s%u",
+			id, dev, cgpu->drv->name, cgpu->device_id);
+
+	if (cgpu->deven == DEV_DISABLED) {
+		message(io_data, MSG_ASCLRDIS, id, NULL, isjson);
+		return;
+	}
+
+	cgpu->deven = DEV_DISABLED;
+
+	message(io_data, MSG_ASCDIS, id, NULL, isjson);
+}
+
+static void ascidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	struct device_drv *drv;
+	int numasc = numascs();
+	int id;
+
+	if (numasc == 0) {
+		message(io_data, MSG_ASCNON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= numasc) {
+		message(io_data, MSG_INVASC, id, NULL, isjson);
+		return;
+	}
+
+	int dev = ascdevice(id);
+	if (dev < 0) { // Should never happen
+		message(io_data, MSG_INVASC, id, NULL, isjson);
+		return;
+	}
+
+	cgpu = get_devices(dev);
+	drv = cgpu->drv;
+
+	if (!drv->identify_device)
+		message(io_data, MSG_ASCNOID, id, NULL, isjson);
+	else {
+		drv->identify_device(cgpu);
+		message(io_data, MSG_ASCIDENT, id, NULL, isjson);
+	}
+}
+#endif
+
+static void asccount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	bool io_open;
+	int count = 0;
+
+#ifdef HAVE_AN_ASIC
+	count = numascs();
+#endif
+
+	message(io_data, MSG_NUMASC, 0, NULL, isjson);
+	io_open = io_add(io_data, isjson ? COMSTR JSON_ASCS : _ASCS COMSTR);
+
+	root = api_add_int(root, "Count", &count, false);
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+#ifdef HAVE_AN_ASIC
+static void ascset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct cgpu_info *cgpu;
+	struct device_drv *drv;
+	char buf[TMPBUFSIZ];
+	int numasc = numascs();
+
+	if (numasc == 0) {
+		message(io_data, MSG_ASCNON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	char *opt = strchr(param, ',');
+	if (opt)
+		*(opt++) = '\0';
+	if (!opt || !*opt) {
+		message(io_data, MSG_MISASCOPT, 0, NULL, isjson);
+		return;
+	}
+
+	int id = atoi(param);
+	if (id < 0 || id >= numasc) {
+		message(io_data, MSG_INVASC, id, NULL, isjson);
+		return;
+	}
+
+	int dev = ascdevice(id);
+	if (dev < 0) { // Should never happen
+		message(io_data, MSG_INVASC, id, NULL, isjson);
+		return;
+	}
+
+	cgpu = get_devices(dev);
+	drv = cgpu->drv;
+
+	char *set = strchr(opt, ',');
+	if (set)
+		*(set++) = '\0';
+
+	if (!drv->set_device)
+		message(io_data, MSG_ASCNOSET, id, NULL, isjson);
+	else {
+		char *ret = drv->set_device(cgpu, opt, set, buf, sizeof(buf));
+		if (ret) {
+			if (strcasecmp(opt, "help") == 0)
+				message(io_data, MSG_ASCHELP, id, ret, isjson);
+			else
+				message(io_data, MSG_ASCSETERR, id, ret, isjson);
+		} else
+			message(io_data, MSG_ASCSETOK, id, NULL, isjson);
+	}
+}
+#endif
+
+static void lcddata(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	struct cgpu_info *cgpu;
+	bool io_open;
+	double ghs = 0.0, last_share_diff = 0.0;
+	float temp = 0.0;
+	time_t last_share_time = 0;
+	time_t last_device_valid_work = 0;
+	struct pool *pool = NULL;
+	char *rpc_url = "none", *rpc_user = "";
+	int i;
+
+	message(io_data, MSG_LCD, 0, NULL, isjson);
+	io_open = io_add(io_data, isjson ? COMSTR JSON_LCD : _LCD COMSTR);
+
+	// stop hashmeter() changing some while copying
+	mutex_lock(&hash_lock);
+
+	root = api_add_elapsed(root, "Elapsed", &(total_secs), true);
+	ghs = total_mhashes_done / total_secs / 1000.0;
+	root = api_add_mhs(root, "GHS av", &ghs, true);
+	ghs = rolling5 / 1000.0;
+	root = api_add_mhs(root, "GHS 5m", &ghs, true);
+	ghs = total_rolling / 1000.0;
+	root = api_add_mhs(root, "GHS 5s", &ghs, true);
+
+	mutex_unlock(&hash_lock);
+
+	temp = 0;
+	last_device_valid_work = 0;
+	for (i = 0; i < total_devices; i++) {
+		cgpu = get_devices(i);
+		if (last_device_valid_work == 0 ||
+		    last_device_valid_work < cgpu->last_device_valid_work)
+			last_device_valid_work = cgpu->last_device_valid_work;
+		if (temp < cgpu->temp)
+			temp = cgpu->temp;
+	}
+
+	last_share_time = 0;
+	last_share_diff = 0;
+	for (i = 0; i < total_pools; i++) {
+		pool = pools[i];
+
+		if (pool->removed)
+			continue;
+
+		if (last_share_time == 0 || last_share_time < pool->last_share_time) {
+			last_share_time = pool->last_share_time;
+			last_share_diff = pool->last_share_diff;
+		}
+	}
+	pool = current_pool();
+	if (pool) {
+		rpc_url = pool->rpc_url;
+		rpc_user = pool->rpc_user;
+	}
+
+	root = api_add_temp(root, "Temperature", &temp, false);
+	root = api_add_diff(root, "Last Share Difficulty", &last_share_diff, false);
+	root = api_add_time(root, "Last Share Time", &last_share_time, false);
+	root = api_add_uint64(root, "Best Share", &best_diff, true);
+	root = api_add_time(root, "Last Valid Work", &last_device_valid_work, false);
+	root = api_add_uint(root, "Found Blocks", &found_blocks, true);
+	root = api_add_escape(root, "Current Pool", rpc_url, true);
+	root = api_add_escape(root, "User", rpc_user, true);
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group);
+
+struct CMDS {
+	char *name;
+	void (*func)(struct io_data *, SOCKETTYPE, char *, bool, char);
+	bool iswritemode;
+	bool joinable;
+} cmds[] = {
+	{ "version",		apiversion,	false,	true },
+	{ "config",		minerconfig,	false,	true },
+	{ "devs",		devstatus,	false,	true },
+	{ "edevs",		edevstatus,	false,	true },
+	{ "pools",		poolstatus,	false,	true },
+	{ "summary",		summary,	false,	true },
+#ifdef HAVE_AN_FPGA
+	{ "pga",		pgadev,		false,	false },
+	{ "pgaenable",		pgaenable,	true,	false },
+	{ "pgadisable",		pgadisable,	true,	false },
+	{ "pgaidentify",	pgaidentify,	true,	false },
+#endif
+	{ "pgacount",		pgacount,	false,	true },
+	{ "switchpool",		switchpool,	true,	false },
+	{ "addpool",		addpool,	true,	false },
+	{ "poolpriority",	poolpriority,	true,	false },
+	{ "poolquota",		poolquota,	true,	false },
+	{ "enablepool",		enablepool,	true,	false },
+	{ "disablepool",	disablepool,	true,	false },
+	{ "removepool",		removepool,	true,	false },
+	{ "save",		dosave,		true,	false },
+	{ "quit",		doquit,		true,	false },
+	{ "privileged",		privileged,	true,	false },
+	{ "notify",		notify,		false,	true },
+	{ "devdetails",		devdetails,	false,	true },
+	{ "restart",		dorestart,	true,	false },
+	{ "stats",		minerstats,	false,	true },
+	{ "dbgstats",		minerdebug,	false,	true },
+	{ "estats",		minerestats,	false,	true },
+	{ "check",		checkcommand,	false,	false },
+	{ "failover-only",	failoveronly,	true,	false },
+	{ "coin",		minecoin,	false,	true },
+	{ "debug",		debugstate,	true,	false },
+	{ "setconfig",		setconfig,	true,	false },
+	{ "usbstats",		usbstats,	false,	true },
+#ifdef HAVE_AN_FPGA
+	{ "pgaset",		pgaset,		true,	false },
+#endif
+	{ "zero",		dozero,		true,	false },
+	{ "hotplug",		dohotplug,	true,	false },
+#ifdef HAVE_AN_ASIC
+	{ "asc",		ascdev,		false,	false },
+	{ "ascenable",		ascenable,	true,	false },
+	{ "ascdisable",		ascdisable,	true,	false },
+	{ "ascidentify",	ascidentify,	true,	false },
+	{ "ascset",		ascset,		true,	false },
+#endif
+	{ "asccount",		asccount,	false,	true },
+	{ "lcd",		lcddata,	false,	true },
+	{ "lockstats",		lockstats,	true,	true },
+	{ NULL,			NULL,		false,	false }
+};
+
+static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group)
+{
+	struct api_data *root = NULL;
+	bool io_open;
+	char cmdbuf[100];
+	bool found, access;
+	int i;
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISCHK, 0, NULL, isjson);
+		return;
+	}
+
+	found = false;
+	access = false;
+	for (i = 0; cmds[i].name != NULL; i++) {
+		if (strcmp(cmds[i].name, param) == 0) {
+			found = true;
+
+			snprintf(cmdbuf, sizeof(cmdbuf), "|%s|", param);
+			if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
+				access = true;
+
+			break;
+		}
+	}
+
+	message(io_data, MSG_CHECK, 0, NULL, isjson);
+	io_open = io_add(io_data, isjson ? COMSTR JSON_CHECK : _CHECK COMSTR);
+
+	root = api_add_const(root, "Exists", found ? YES : NO, false);
+	root = api_add_const(root, "Access", access ? YES : NO, false);
+
+	root = print_data(io_data, root, isjson, false);
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
+static void head_join(struct io_data *io_data, char *cmdptr, bool isjson, bool *firstjoin)
+{
+	char *ptr;
+
+	if (*firstjoin) {
+		if (isjson)
+			io_add(io_data, JSON0);
+		*firstjoin = false;
+	} else {
+		if (isjson)
+			io_add(io_data, JSON_BETWEEN_JOIN);
+	}
+
+	// External supplied string
+	ptr = escape_string(cmdptr, isjson);
+
+	if (isjson) {
+		io_add(io_data, JSON1);
+		io_add(io_data, ptr);
+		io_add(io_data, JSON2);
+	} else {
+		io_add(io_data, JOIN_CMD);
+		io_add(io_data, ptr);
+		io_add(io_data, BETWEEN_JOIN);
+	}
+
+	if (ptr != cmdptr)
+		free(ptr);
+}
+
+static void tail_join(struct io_data *io_data, bool isjson)
+{
+	if (io_data->close) {
+		io_add(io_data, JSON_CLOSE);
+		io_data->close = false;
+	}
+
+	if (isjson) {
+		io_add(io_data, JSON_END);
+		io_add(io_data, JSON3);
+	}
+}
+
+static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson)
+{
+	int count, sendc, res, tosend, len, n;
+	char *buf = io_data->ptr;
+
+	strcpy(buf, io_data->ptr);
+
+	if (io_data->close)
+		strcat(buf, JSON_CLOSE);
+
+	if (isjson)
+		strcat(buf, JSON_END);
+
+	len = strlen(buf);
+	tosend = len+1;
+
+	applog(LOG_DEBUG, "API: send reply: (%d) '%.10s%s'", tosend, buf, len > 10 ? "..." : BLANK);
+
+	count = sendc = 0;
+	while (count < 5 && tosend > 0) {
+		// allow 50ms per attempt
+		struct timeval timeout = {0, 50000};
+		fd_set wd;
+
+		FD_ZERO(&wd);
+		FD_SET(c, &wd);
+		if ((res = select(c + 1, NULL, &wd, NULL, &timeout)) < 1) {
+			applog(LOG_WARNING, "API: send select failed (%d)", res);
+			return;
+		}
+
+		n = send(c, buf, tosend, 0);
+		sendc++;
+
+		if (SOCKETFAIL(n)) {
+			count++;
+			if (sock_blocks())
+				continue;
+
+			applog(LOG_WARNING, "API: send (%d:%d) failed: %s", len+1, (len+1 - tosend), SOCKERRMSG);
+
+			return;
+		} else {
+			if (sendc <= 1) {
+				if (n == tosend)
+					applog(LOG_DEBUG, "API: sent all of %d first go", tosend);
+				else
+					applog(LOG_DEBUG, "API: sent %d of %d first go", n, tosend);
+			} else {
+				if (n == tosend)
+					applog(LOG_DEBUG, "API: sent all of remaining %d (sendc=%d)", tosend, sendc);
+				else
+					applog(LOG_DEBUG, "API: sent %d of remaining %d (sendc=%d)", n, tosend, sendc);
+			}
+
+			tosend -= n;
+			buf += n;
+
+			if (n == 0)
+				count++;
+		}
+	}
+}
+
+static void tidyup(__maybe_unused void *arg)
+{
+	mutex_lock(&quit_restart_lock);
+
+	SOCKETTYPE *apisock = (SOCKETTYPE *)arg;
+
+	bye = true;
+
+	if (*apisock != INVSOCK) {
+		shutdown(*apisock, SHUT_RDWR);
+		CLOSESOCKET(*apisock);
+		*apisock = INVSOCK;
+	}
+
+	if (ipaccess != NULL) {
+		free(ipaccess);
+		ipaccess = NULL;
+	}
+
+	io_free();
+
+	mutex_unlock(&quit_restart_lock);
+}
+
+/*
+ * Interpret --api-groups G:cmd1:cmd2:cmd3,P:cmd4,*,...
+ */
+static void setup_groups()
+{
+	char *api_groups = opt_api_groups ? opt_api_groups : (char *)BLANK;
+	char *buf, *ptr, *next, *colon;
+	char group;
+	char commands[TMPBUFSIZ];
+	char cmdbuf[100];
+	char *cmd;
+	bool addstar, did;
+	int i;
+
+	buf = cgmalloc(strlen(api_groups) + 1);
+
+	strcpy(buf, api_groups);
+
+	next = buf;
+	// for each group defined
+	while (next && *next) {
+		ptr = next;
+		next = strchr(ptr, ',');
+		if (next)
+			*(next++) = '\0';
+
+		// Validate the group
+		if (*(ptr+1) != ':') {
+			colon = strchr(ptr, ':');
+			if (colon)
+				*colon = '\0';
+			quit(1, "API invalid group name '%s'", ptr);
+		}
+
+		group = GROUP(*ptr);
+		if (!VALIDGROUP(group))
+			quit(1, "API invalid group name '%c'", *ptr);
+
+		if (group == PRIVGROUP)
+			quit(1, "API group name can't be '%c'", PRIVGROUP);
+
+		if (group == NOPRIVGROUP)
+			quit(1, "API group name can't be '%c'", NOPRIVGROUP);
+
+		if (apigroups[GROUPOFFSET(group)].commands != NULL)
+			quit(1, "API duplicate group name '%c'", *ptr);
+
+		ptr += 2;
+
+		// Validate the command list (and handle '*')
+		cmd = &(commands[0]);
+		*(cmd++) = SEPARATOR;
+		*cmd = '\0';
+		addstar = false;
+		while (ptr && *ptr) {
+			colon = strchr(ptr, ':');
+			if (colon)
+				*(colon++) = '\0';
+
+			if (strcmp(ptr, "*") == 0)
+				addstar = true;
+			else {
+				did = false;
+				for (i = 0; cmds[i].name != NULL; i++) {
+					if (strcasecmp(ptr, cmds[i].name) == 0) {
+						did = true;
+						break;
+					}
+				}
+				if (did) {
+					// skip duplicates
+					snprintf(cmdbuf, sizeof(cmdbuf), "|%s|", cmds[i].name);
+					if (strstr(commands, cmdbuf) == NULL) {
+						strcpy(cmd, cmds[i].name);
+						cmd += strlen(cmds[i].name);
+						*(cmd++) = SEPARATOR;
+						*cmd = '\0';
+					}
+				} else {
+					quit(1, "API unknown command '%s' in group '%c'", ptr, group);
+				}
+			}
+
+			ptr = colon;
+		}
+
+		// * = allow all non-iswritemode commands
+		if (addstar) {
+			for (i = 0; cmds[i].name != NULL; i++) {
+				if (cmds[i].iswritemode == false) {
+					// skip duplicates
+					snprintf(cmdbuf, sizeof(cmdbuf), "|%s|", cmds[i].name);
+					if (strstr(commands, cmdbuf) == NULL) {
+						strcpy(cmd, cmds[i].name);
+						cmd += strlen(cmds[i].name);
+						*(cmd++) = SEPARATOR;
+						*cmd = '\0';
+					}
+				}
+			}
+		}
+
+		ptr = apigroups[GROUPOFFSET(group)].commands = cgmalloc(strlen(commands) + 1);
+
+		strcpy(ptr, commands);
+	}
+
+	// Now define R (NOPRIVGROUP) as all non-iswritemode commands
+	cmd = &(commands[0]);
+	*(cmd++) = SEPARATOR;
+	*cmd = '\0';
+	for (i = 0; cmds[i].name != NULL; i++) {
+		if (cmds[i].iswritemode == false) {
+			strcpy(cmd, cmds[i].name);
+			cmd += strlen(cmds[i].name);
+			*(cmd++) = SEPARATOR;
+			*cmd = '\0';
+		}
+	}
+
+	ptr = apigroups[GROUPOFFSET(NOPRIVGROUP)].commands = cgmalloc(strlen(commands) + 1);
+
+	strcpy(ptr, commands);
+
+	// W (PRIVGROUP) is handled as a special case since it simply means all commands
+
+	free(buf);
+	return;
+}
+
+/*
+ * Interpret [W:]IP[/Prefix][,[R|W:]IP2[/Prefix2][,...]] --api-allow option
+ *  ipv6 address should be enclosed with a pair of square brackets and the prefix left outside
+ *	special case of 0/0 allows /0 (means all IP addresses)
+ */
+#define ALLIP "0/0"
+/*
+ * N.B. IP4 addresses are by Definition 32bit big endian on all platforms
+ */
+static void setup_ipaccess()
+{
+	char *buf, *ptr, *comma, *slash, *end, *dot;
+	int ipcount, mask, i, shift;
+	char tmp[64], original[64];
+	bool ipv6 = false;
+	char group;
+
+	buf = cgmalloc(strlen(opt_api_allow) + 1);
+
+	strcpy(buf, opt_api_allow);
+
+	ipcount = 1;
+	ptr = buf;
+	while (*ptr)
+		if (*(ptr++) == ',')
+			ipcount++;
+
+	// possibly more than needed, but never less
+	ipaccess = cgcalloc(ipcount, sizeof(struct IPACCESS));
+
+	ips = 0;
+	ptr = buf;
+	while (ptr && *ptr) {
+		while (*ptr == ' ' || *ptr == '\t')
+			ptr++;
+
+		if (*ptr == ',') {
+			ptr++;
+			continue;
+		}
+
+		comma = strchr(ptr, ',');
+		if (comma)
+			*(comma++) = '\0';
+
+		strncpy(original, ptr, sizeof(original));
+		original[sizeof(original)-1] = '\0';
+		group = NOPRIVGROUP;
+
+		if (isalpha(*ptr) && *(ptr+1) == ':') {
+			if (DEFINEDGROUP(*ptr))
+				group = GROUP(*ptr);
+
+			ptr += 2;
+		}
+
+		ipaccess[ips].group = group;
+
+		if (strcmp(ptr, ALLIP) == 0) {
+			for (i = 0; i < 16; i++) {
+				ipaccess[ips].ip.s6_addr[i] = 0;
+				ipaccess[ips].mask.s6_addr[i] = 0;
+			}
+		}
+		else {
+			end = strchr(ptr, '/');
+			if (!end) {
+				for (i = 0; i < 16; i++)
+					ipaccess[ips].mask.s6_addr[i] = 0xff;
+				end = ptr + strlen(ptr);
+			}
+			slash = end--;
+			if (*ptr == '[' && *end == ']') {
+				*(ptr++) = '\0';
+				*(end--) = '\0';
+				ipv6 = true;
+			}
+			else
+				ipv6 = false;
+			if (*slash) {
+				*(slash++) = '\0';
+				mask = atoi(slash);
+				if (mask < 1 || (mask += ipv6 ? 0 : 96) > 128) {
+					applog(LOG_ERR, "API: ignored address with "
+							"invalid mask (%d) '%s'",
+							mask, original);
+					goto popipo; // skip invalid/zero
+				}
+
+				for (i = 0; i < 16; i++)
+					ipaccess[ips].mask.s6_addr[i] = 0;
+
+				i = 0;
+				shift = 7;
+				while (mask-- > 0) {
+					ipaccess[ips].mask.s6_addr[i] |= 1 << shift;
+					if (shift-- == 0) {
+						i++;
+						shift = 7;
+					}
+				}
+			}
+
+			for (i = 0; i < 16; i++)
+				ipaccess[ips].ip.s6_addr[i] = 0; // missing default to '[::]'
+			if (ipv6) {
+				if (INET_PTON(AF_INET6, ptr, &(ipaccess[ips].ip)) != 1) {
+					applog(LOG_ERR, "API: ignored invalid "
+							"IPv6 address '%s'",
+							original);
+					goto popipo;
+				}
+			}
+			else {
+				/* v4 mapped v6 address,
+				 * such as "::ffff:255.255.255.255"
+				 * but pad on extra missing .0 as needed */
+				dot = strchr(ptr, '.');
+				if (!dot) {
+					snprintf(tmp, sizeof(tmp),
+						 "::ffff:%s.0.0.0",
+						 ptr);
+				} else {
+					dot = strchr(dot+1, '.');
+					if (!dot) {
+						snprintf(tmp, sizeof(tmp),
+							 "::ffff:%s.0.0",
+							 ptr);
+					} else {
+						dot = strchr(dot+1, '.');
+						if (!dot) {
+							snprintf(tmp, sizeof(tmp),
+								 "::ffff:%s.0",
+								 ptr);
+						} else {
+							snprintf(tmp, sizeof(tmp),
+								 "::ffff:%s",
+								 ptr);
+						}
+					}
+				}
+				if (INET_PTON(AF_INET6, tmp, &(ipaccess[ips].ip)) != 1) {
+					applog(LOG_ERR, "API: ignored invalid "
+							"IPv4 address '%s' (as %s)",
+							original, tmp);
+					goto popipo;
+				}
+			}
+			for (i = 0; i < 16; i++)
+				ipaccess[ips].ip.s6_addr[i] &= ipaccess[ips].mask.s6_addr[i];
+		}
+
+		ips++;
+popipo:
+		ptr = comma;
+	}
+
+	free(buf);
+}
+
+static void *quit_thread(__maybe_unused void *userdata)
+{
+	// allow thread creator to finish whatever it's doing
+	mutex_lock(&quit_restart_lock);
+	mutex_unlock(&quit_restart_lock);
+
+	if (opt_debug)
+		applog(LOG_DEBUG, "API: killing cgminer");
+
+	kill_work();
+
+	return NULL;
+}
+
+static void *restart_thread(__maybe_unused void *userdata)
+{
+	// allow thread creator to finish whatever it's doing
+	mutex_lock(&quit_restart_lock);
+	mutex_unlock(&quit_restart_lock);
+
+	if (opt_debug)
+		applog(LOG_DEBUG, "API: restarting cgminer");
+
+	app_restart();
+
+	return NULL;
+}
+
+static bool check_connect(struct sockaddr_storage *cli, char **connectaddr, char *group)
+{
+	bool addrok = false;
+	int i, j;
+	bool match;
+	char tmp[30];
+	struct in6_addr client_ip;
+
+	*connectaddr = cgmalloc(INET6_ADDRSTRLEN);
+	getnameinfo((struct sockaddr *)cli, sizeof(*cli),
+			*connectaddr, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+
+	// v4 mapped v6 address, such as "::ffff:255.255.255.255"
+	if (cli->ss_family == AF_INET) {
+		snprintf(tmp, sizeof(tmp), "::ffff:%s", *connectaddr);
+		INET_PTON(AF_INET6, tmp, &client_ip);
+	}
+	else
+		INET_PTON(AF_INET6, *connectaddr, &client_ip);
+
+	*group = NOPRIVGROUP;
+	if (opt_api_allow) {
+		for (i = 0; i < ips; i++) {
+			match = true;
+			for (j = 0; j < 16; j++) {
+				if ((client_ip.s6_addr[j] & ipaccess[i].mask.s6_addr[j])
+						!= ipaccess[i].ip.s6_addr[j]) {
+					match = false;
+					break;
+				}
+			}
+			if (match) {
+				addrok = true;
+				*group = ipaccess[i].group;
+				break;
+			}
+		}
+	} else {
+		if (opt_api_network)
+			addrok = true;
+		else
+			addrok = (strcmp(*connectaddr, localaddr) == 0)
+				|| IN6_IS_ADDR_LOOPBACK(&client_ip);
+	}
+
+	return addrok;
+}
+
+static void mcast()
+{
+	struct sockaddr_storage came_from;
+	time_t bindstart;
+	char *binderror;
+	SOCKETTYPE mcast_sock = INVSOCK;
+	SOCKETTYPE reply_sock = INVSOCK;
+	socklen_t came_from_siz;
+	char *connectaddr;
+	ssize_t rep;
+	int bound;
+	int count;
+	int reply_port;
+	bool addrok;
+	char group;
+
+	char port_s[10], came_from_port[10];
+	struct addrinfo hints, *res, *host, *client;
+
+	char expect[] = "cgminer-"; // first 8 bytes constant
+	char *expect_code;
+	size_t expect_code_len;
+	char buf[1024];
+	char replybuf[1024];
+
+	snprintf(port_s, sizeof(port_s), "%d", opt_api_mcast_port);
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	if (getaddrinfo(opt_api_mcast_addr, port_s, &hints, &res) != 0)
+		quit(1, "Invalid API Multicast Address");
+	host = res;
+	while (host != NULL) {
+		mcast_sock = socket(res->ai_family, SOCK_DGRAM, 0);
+		if (mcast_sock > 0)
+			break;
+		host = host->ai_next;
+	}
+	if (mcast_sock == INVSOCK) {
+		freeaddrinfo(res);
+		quit(1, "API mcast could not open socket");
+	}
+
+	int optval = 1;
+	if (SOCKETFAIL(setsockopt(mcast_sock, SOL_SOCKET, SO_REUSEADDR, (void *)(&optval), sizeof(optval)))) {
+		applog(LOG_ERR, "API mcast setsockopt SO_REUSEADDR failed (%s)%s", SOCKERRMSG, MUNAVAILABLE);
+		goto die;
+	}
+
+	// try for more than 1 minute ... in case the old one hasn't completely gone yet
+	bound = 0;
+	bindstart = time(NULL);
+	while (bound == 0) {
+		if (SOCKETFAIL(bind(mcast_sock, host->ai_addr, host->ai_addrlen))) {
+			binderror = SOCKERRMSG;
+			if ((time(NULL) - bindstart) > 61)
+				break;
+			else
+				cgsleep_ms(30000);
+		} else
+			bound = 1;
+	}
+
+	if (bound == 0) {
+		applog(LOG_ERR, "API mcast bind to port %d failed (%s)%s", opt_api_mcast_port, binderror, MUNAVAILABLE);
+		goto die;
+	}
+
+	switch (host->ai_family) {
+		case AF_INET: {
+			struct ip_mreq grp;
+			memset(&grp, 0, sizeof(grp));
+			grp.imr_multiaddr.s_addr = ((struct sockaddr_in *)(host->ai_addr))->sin_addr.s_addr;
+			grp.imr_interface.s_addr = INADDR_ANY;
+
+			if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+							(void *)(&grp), sizeof(grp)))) {
+				applog(LOG_ERR, "API mcast join failed (%s)%s", SOCKERRMSG, MUNAVAILABLE);
+				goto die;
+			}
+			break;
+		}
+		case AF_INET6: {
+			struct ipv6_mreq grp;
+			memcpy(&grp.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)(host->ai_addr))->sin6_addr),
+					sizeof(struct in6_addr));
+			grp.ipv6mr_interface= 0;
+
+			if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
+							(void *)(&grp), sizeof(grp)))) {
+				applog(LOG_ERR, "API mcast join failed (%s)%s", SOCKERRMSG, MUNAVAILABLE);
+				goto die;
+			}
+			break;
+		}
+		default:
+			break;
+	}
+	freeaddrinfo(res);
+
+	expect_code_len = sizeof(expect) + strlen(opt_api_mcast_code);
+	expect_code = cgmalloc(expect_code_len + 1);
+	snprintf(expect_code, expect_code_len+1, "%s%s-", expect, opt_api_mcast_code);
+
+	count = 0;
+	while (80085) {
+		cgsleep_ms(1000);
+
+		count++;
+		came_from_siz = sizeof(came_from);
+		if (SOCKETFAIL(rep = recvfrom(mcast_sock, buf, sizeof(buf) - 1,
+						0, (struct sockaddr *)(&came_from), &came_from_siz))) {
+			applog(LOG_DEBUG, "API mcast failed count=%d (%s) (%d)",
+					count, SOCKERRMSG, (int)mcast_sock);
+			continue;
+		}
+
+		addrok = check_connect(&came_from, &connectaddr, &group);
+		applog(LOG_DEBUG, "API mcast from %s - %s",
+					connectaddr, addrok ? "Accepted" : "Ignored");
+		if (!addrok)
+			continue;
+
+		buf[rep] = '\0';
+		if (rep > 0 && buf[rep-1] == '\n')
+			buf[--rep] = '\0';
+
+		getnameinfo((struct sockaddr *)(&came_from), came_from_siz,
+				NULL, 0, came_from_port, sizeof(came_from_port), NI_NUMERICHOST);
+
+		applog(LOG_DEBUG, "API mcast request rep=%d (%s) from [%s]:%s",
+					(int)rep, buf, connectaddr, came_from_port);
+
+		if ((size_t)rep > expect_code_len && memcmp(buf, expect_code, expect_code_len) == 0) {
+			reply_port = atoi(&buf[expect_code_len]);
+			if (reply_port < 1 || reply_port > 65535) {
+				applog(LOG_DEBUG, "API mcast request ignored - invalid port (%s)",
+							&buf[expect_code_len]);
+			} else {
+				applog(LOG_DEBUG, "API mcast request OK port %s=%d",
+							&buf[expect_code_len], reply_port);
+
+				if (getaddrinfo(connectaddr, &buf[expect_code_len], &hints, &res) != 0) {
+					applog(LOG_ERR, "Invalid client address %s", connectaddr);
+					continue;
+				}
+				client = res;
+				while (client) {
+					reply_sock = socket(res->ai_family, SOCK_DGRAM, 0);
+					if (mcast_sock > 0)
+						break;
+					client = client->ai_next;
+				}
+				if (reply_sock == INVSOCK) {
+					freeaddrinfo(res);
+					applog(LOG_ERR, "API mcast could not open socket to client %s", connectaddr);
+					continue;
+				}
+
+				snprintf(replybuf, sizeof(replybuf),
+							"cgm-" API_MCAST_CODE "-%d-%s",
+							opt_api_port, opt_api_mcast_des);
+
+				rep = sendto(reply_sock, replybuf, strlen(replybuf)+1,
+						0, client->ai_addr, client->ai_addrlen);
+				freeaddrinfo(res);
+				if (SOCKETFAIL(rep)) {
+					applog(LOG_DEBUG, "API mcast send reply failed (%s) (%d)",
+								SOCKERRMSG, (int)reply_sock);
+				} else {
+					applog(LOG_DEBUG, "API mcast send reply (%s) succeeded (%d) (%d)",
+								replybuf, (int)rep, (int)reply_sock);
+				}
+
+				CLOSESOCKET(reply_sock);
+			}
+		} else
+			applog(LOG_DEBUG, "API mcast request was no good");
+	}
+
+die:
+
+	CLOSESOCKET(mcast_sock);
+}
+
+static void *mcast_thread(void *userdata)
+{
+	struct thr_info *mythr = userdata;
+
+	pthread_detach(pthread_self());
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+	RenameThread("APIMcast");
+
+	mcast();
+
+	PTH(mythr) = 0L;
+
+	return NULL;
+}
+
+void mcast_init()
+{
+	struct thr_info *thr;
+
+	thr = cgcalloc(1, sizeof(*thr));
+
+	if (thr_info_create(thr, NULL, mcast_thread, thr))
+		quit(1, "API mcast thread create failed");
+}
+
+#ifdef USE_BITMAIN_SOC
+void reCalculateAVG()
+{
+	new_total_mhashes_done = total_mhashes_done;
+	if(total_secs>0)
+		new_total_secs = total_secs-1;
+	else new_total_secs=total_secs;
+}
+#endif
+
+void api(int api_thr_id)
+{
+	struct io_data *io_data;
+	struct thr_info bye_thr;
+	char buf[TMPBUFSIZ];
+	char param_buf[TMPBUFSIZ];
+	SOCKETTYPE c;
+	int n, bound;
+	char *connectaddr;
+	char *binderror;
+	time_t bindstart;
+	short int port = opt_api_port;
+	char port_s[10];
+	struct sockaddr_storage cli;
+	socklen_t clisiz;
+	char cmdbuf[100];
+	char *cmd = NULL;
+	char *param;
+	bool addrok;
+	char group;
+	json_error_t json_err;
+	json_t *json_config;
+	json_t *json_val;
+	bool isjson;
+	bool did, isjoin, firstjoin;
+	int i;
+	struct addrinfo hints, *res, *host;
+	SOCKETTYPE *apisock;
+
+	apisock = cgmalloc(sizeof(*apisock));
+	*apisock = INVSOCK;
+	json_config = NULL;
+	isjoin = false;
+
+	if (!opt_api_listen) {
+		applog(LOG_DEBUG, "API not running%s", UNAVAILABLE);
+		free(apisock);
+		return;
+	}
+
+	io_data = sock_io_new();
+
+	mutex_init(&quit_restart_lock);
+
+	pthread_cleanup_push(tidyup, (void *)apisock);
+	my_thr_id = api_thr_id;
+
+	setup_groups();
+
+	if (opt_api_allow) {
+		setup_ipaccess();
+
+		if (ips == 0) {
+			applog(LOG_WARNING, "API not running (no valid IPs specified)%s", UNAVAILABLE);
+			free(apisock);
+			return;
+		}
+	}
+
+	/* This should be done before curl in needed
+	 * to ensure curl has already called WSAStartup() in windows */
+	cgsleep_ms(opt_log_interval*1000);
+
+	snprintf(port_s, sizeof(port_s), "%d", port);
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_flags = AI_PASSIVE;
+	hints.ai_family = AF_UNSPEC;
+	if (getaddrinfo(opt_api_host, port_s, &hints, &res) != 0) {
+		applog(LOG_ERR, "API failed to resolve %s", opt_api_host);
+		free(apisock);
+		return;
+	}
+	host = res;
+	while (host) {
+		*apisock = socket(res->ai_family, SOCK_STREAM, 0);
+		if (*apisock > 0)
+			break;
+		host = host->ai_next;
+	}
+	if (*apisock == INVSOCK) {
+		applog(LOG_ERR, "API initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
+		freeaddrinfo(res);
+		free(apisock);
+		return;
+	}
+
+#ifndef WIN32
+	// On linux with SO_REUSEADDR, bind will get the port if the previous
+	// socket is closed (even if it is still in TIME_WAIT) but fail if
+	// another program has it open - which is what we want
+	int optval = 1;
+	// If it doesn't work, we don't really care - just show a debug message
+	if (SOCKETFAIL(setsockopt(*apisock, SOL_SOCKET, SO_REUSEADDR, (void *)(&optval), sizeof(optval))))
+		applog(LOG_DEBUG, "API setsockopt SO_REUSEADDR failed (ignored): %s", SOCKERRMSG);
+#else
+	// On windows a 2nd program can bind to a port>1024 already in use unless
+	// SO_EXCLUSIVEADDRUSE is used - however then the bind to a closed port
+	// in TIME_WAIT will fail until the timeout - so we leave the options alone
+#endif
+
+	// try for more than 1 minute ... in case the old one hasn't completely gone yet
+	bound = 0;
+	bindstart = time(NULL);
+	while (bound == 0) {
+		if (SOCKETFAIL(bind(*apisock, host->ai_addr, host->ai_addrlen))) {
+			binderror = SOCKERRMSG;
+			if ((time(NULL) - bindstart) > 61)
+				break;
+			else {
+				applog(LOG_WARNING, "API bind to port %d failed - trying again in 30sec", port);
+				cgsleep_ms(30000);
+			}
+		} else
+			bound = 1;
+	}
+	freeaddrinfo(res);
+
+	if (bound == 0) {
+		applog(LOG_ERR, "API bind to port %d failed (%s)%s", port, binderror, UNAVAILABLE);
+		free(apisock);
+		return;
+	}
+
+	if (SOCKETFAIL(listen(*apisock, QUEUE))) {
+		applog(LOG_ERR, "API3 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
+		CLOSESOCKET(*apisock);
+		free(apisock);
+		return;
+	}
+
+	if (opt_api_allow)
+		applog(LOG_WARNING, "API running in IP access mode on port %d (%d)", port, (int)*apisock);
+	else {
+		if (opt_api_network)
+			applog(LOG_WARNING, "API running in UNRESTRICTED read access mode on port %d (%d)", port, (int)*apisock);
+		else
+			applog(LOG_WARNING, "API running in local read access mode on port %d (%d)", port, (int)*apisock);
+	}
+
+	if (opt_api_mcast)
+		mcast_init();
+
+	strbufs = k_new_list("StrBufs", sizeof(SBITEM), ALLOC_SBITEMS, LIMIT_SBITEMS, false);
+
+	while (!bye) {
+		clisiz = sizeof(cli);
+		if (SOCKETFAIL(c = accept(*apisock, (struct sockaddr *)(&cli), &clisiz))) {
+			applog(LOG_ERR, "API failed (%s)%s (%d)", SOCKERRMSG, UNAVAILABLE, (int)*apisock);
+			goto die;
+		}
+
+		addrok = check_connect((struct sockaddr_storage *)&cli, &connectaddr, &group);
+		applog(LOG_DEBUG, "API: connection from %s - %s",
+					connectaddr, addrok ? "Accepted" : "Ignored");
+
+		if (addrok) {
+			/* Accept only half the TMPBUFSIZ to account for space
+			 * potentially used by escaping chars. */
+			n = recv(c, &buf[0], TMPBUFSIZ / 2 - 1, 0);
+			if (SOCKETFAIL(n))
+				buf[0] = '\0';
+			else
+				buf[n] = '\0';
+
+			if (opt_debug) {
+				if (SOCKETFAIL(n))
+					applog(LOG_DEBUG, "API: recv failed: %s", SOCKERRMSG);
+				else
+					applog(LOG_DEBUG, "API: recv command: (%d) '%s'", n, buf);
+			}
+
+			if (!SOCKETFAIL(n)) {
+				// the time of the request in now
+				when = time(NULL);
+				io_reinit(io_data);
+
+				did = false;
+
+				if (*buf != ISJSON) {
+					isjson = false;
+
+					param = strchr(buf, SEPARATOR);
+					if (param != NULL)
+						*(param++) = '\0';
+
+					cmd = buf;
+				}
+				else {
+					isjson = true;
+
+					param = NULL;
+
+					json_config = json_loadb(buf, n, 0, &json_err);
+
+					if (!json_is_object(json_config)) {
+						message(io_data, MSG_INVJSON, 0, NULL, isjson);
+						send_result(io_data, c, isjson);
+						did = true;
+					} else {
+						json_val = json_object_get(json_config, JSON_COMMAND);
+						if (json_val == NULL) {
+							message(io_data, MSG_MISCMD, 0, NULL, isjson);
+							send_result(io_data, c, isjson);
+							did = true;
+						} else {
+							if (!json_is_string(json_val)) {
+								message(io_data, MSG_INVCMD, 0, NULL, isjson);
+								send_result(io_data, c, isjson);
+								did = true;
+							} else {
+								cmd = (char *)json_string_value(json_val);
+								json_val = json_object_get(json_config, JSON_PARAMETER);
+								if (json_is_string(json_val))
+									param = (char *)json_string_value(json_val);
+								else if (json_is_integer(json_val)) {
+									snprintf(param_buf, sizeof(param_buf),
+										"%d", (int)json_integer_value(json_val));
+									param = param_buf;
+								} else if (json_is_real(json_val)) {
+									snprintf(param_buf, sizeof(param_buf),
+										"%f", (double)json_real_value(json_val));
+									param = param_buf;
+								}
+							}
+						}
+					}
+				}
+
+				if (!did) {
+					char *cmdptr, *cmdsbuf = NULL;
+
+					if (strchr(cmd, CMDJOIN)) {
+						firstjoin = isjoin = true;
+						// cmd + leading+tailing '|' + '\0'
+						cmdsbuf = cgmalloc(strlen(cmd) + 3);
+						strcpy(cmdsbuf, "|");
+						param = NULL;
+					} else
+						firstjoin = isjoin = false;
+
+					cmdptr = cmd;
+					do {
+						did = false;
+						if (isjoin) {
+							cmd = strchr(cmdptr, CMDJOIN);
+							if (cmd)
+								*(cmd++) = '\0';
+							if (!*cmdptr)
+								goto inochi;
+						}
+
+						for (i = 0; cmds[i].name != NULL; i++) {
+							if (strcmp(cmdptr, cmds[i].name) == 0) {
+								snprintf(cmdbuf, sizeof(cmdbuf), "|%s|", cmdptr);
+								if (isjoin) {
+									if (strstr(cmdsbuf, cmdbuf)) {
+										did = true;
+										break;
+									}
+									strcat(cmdsbuf, cmdptr);
+									strcat(cmdsbuf, "|");
+									head_join(io_data, cmdptr, isjson, &firstjoin);
+									if (!cmds[i].joinable) {
+										message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
+										did = true;
+										tail_join(io_data, isjson);
+										break;
+									}
+								}
+								if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
+									(cmds[i].func)(io_data, c, param, isjson, group);
+								else {
+									message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
+									applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name);
+								}
+
+								did = true;
+								if (!isjoin)
+									send_result(io_data, c, isjson);
+								else
+									tail_join(io_data, isjson);
+								break;
+							}
+						}
+
+						if (!did) {
+							if (isjoin)
+								head_join(io_data, cmdptr, isjson, &firstjoin);
+							message(io_data, MSG_INVCMD, 0, NULL, isjson);
+							if (isjoin)
+								tail_join(io_data, isjson);
+							else
+								send_result(io_data, c, isjson);
+						}
+inochi:
+						if (isjoin)
+							cmdptr = cmd;
+					} while (isjoin && cmdptr);
+				}
+
+				if (isjoin)
+					send_result(io_data, c, isjson);
+
+				if (isjson && json_is_object(json_config))
+					json_decref(json_config);
+			}
+		}
+		CLOSESOCKET(c);
+	}
+die:
+	/* Blank line fix for older compilers since pthread_cleanup_pop is a
+	 * macro that gets confused by a label existing immediately before it
+	 */
+	;
+	pthread_cleanup_pop(true);
+
+	free(apisock);
+
+	if (opt_debug)
+		applog(LOG_DEBUG, "API: terminating due to: %s",
+				do_a_quit ? "QUIT" : (do_a_restart ? "RESTART" : (bye ? "BYE" : "UNKNOWN!")));
+
+	mutex_lock(&quit_restart_lock);
+
+	if (do_a_restart) {
+		if (thr_info_create(&bye_thr, NULL, restart_thread, &bye_thr)) {
+			mutex_unlock(&quit_restart_lock);
+			quit(1, "API failed to initiate a restart - aborting");
+		}
+		pthread_detach(bye_thr.pth);
+	} else if (do_a_quit) {
+		if (thr_info_create(&bye_thr, NULL, quit_thread, &bye_thr)) {
+			mutex_unlock(&quit_restart_lock);
+			quit(1, "API failed to initiate a clean quit - aborting");
+		}
+		pthread_detach(bye_thr.pth);
+	}
+
+	mutex_unlock(&quit_restart_lock);
+}

+ 26 - 0
arg-nonnull.h

@@ -0,0 +1,26 @@
+/* A C macro for declaring that specific arguments must not be NULL.
+   Copyright (C) 2009-2011 Free Software Foundation, Inc.
+
+   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.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* _GL_ARG_NONNULL((n,...,m)) tells the compiler and static analyzer tools
+   that the values passed as arguments n, ..., m must be non-NULL pointers.
+   n = 1 stands for the first argument, n = 2 for the second argument etc.  */
+#ifndef _GL_ARG_NONNULL
+# if (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) || __GNUC__ > 3
+#  define _GL_ARG_NONNULL(params) __attribute__ ((__nonnull__ params))
+# else
+#  define _GL_ARG_NONNULL(params)
+# endif
+#endif

+ 9 - 0
autogen.sh

@@ -0,0 +1,9 @@
+#!/bin/sh
+bs_dir=$(cd "$(dirname "$0")"; pwd)
+
+autoreconf -fi "${bs_dir}"
+
+if test -n "$1" && test -z "$NOCONFIGURE" ; then
+	echo 'Configuring...'
+	"$bs_dir"/configure "$@"
+fi

+ 170 - 0
bench_block.h

@@ -0,0 +1,170 @@
+#ifndef __BENCH_BLOCK_H__
+#define __BENCH_BLOCK_H__
+
+/* This contains 32 carefully chosen work items, 16 of which return diff >= 32
+ * at nonces spaced ~ 0x10000000 apart and 16 < diff 32. */
+
+const char bench_hidiffs[16][324] = {
+// 0002108b diff 131
+"000000029c6bf469abe4ad37605c097a860cff3cf5c1ef4377618f74000000000000000082b1514e7b6565941e5824f084292164ec5f97e7ea20c494bd96e524d478977b536dd2261900896c8b100200"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"64e4e3becc01064d808269b330f40f4de82dc92e894d635025daa3e2e2c410b4",
+
+// 1003dacf diff 37
+"00000002e790c23987181950eeb144591c3ac4d06c0705f2801d097600000000000000009ebbce2f5f0d6cc0aca284ecb1059c856ef2f7f42e7edd403d246754ee4c905a536dd2a91900896ccfda0310"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"4a78daf1b5eb3397af1c00dbd9b06659cdc04183c8baaf5be1dbf32f79e00459",
+
+// 200e57b4 diff 3866
+"000000023e91fce7300a792bfbaa0c76e1aa5f9b546c1db582aee4ff0000000000000000f04650a8e748d2e6fde86a8a920b285f3e22398f583700236958323ef9ea8321536dcf431900896cb4570e20"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"0a1d654ae2b06f219ccf4601933fab408de1c3b7c8c9c85e03231d4aaf5a26cd",
+
+// 300f71e2 diff 335
+"000000023e91fce7300a792bfbaa0c76e1aa5f9b546c1db582aee4ff000000000000000074b39134c2930d2f2e7339f9d502c776c44d6ee599f7efebec6c9bbd04787aae536dce561900896ce2710f30"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"94e60c1180022f337232ab3d298f838304b6008ab237cf7e1717f1933407e592",
+
+// 400548ed diff 2670
+"000000023e91fce7300a792bfbaa0c76e1aa5f9b546c1db582aee4ff0000000000000000c5b821fb0b26d63b00cc26e7ac4d6cfd1d3fc109b0db188e7e792e3d18342919536dce501900896ced480540"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"a290eac61642949c00d17f7cd5980abedb8647fc5df9955dcfe4d56a50a0c564",
+
+// 5001f760 diff 60
+"00000002e790c23987181950eeb144591c3ac4d06c0705f2801d097600000000000000006e9d94bf5a0ab7b202d39e1200af96074e4f641f4e55e3e9e3aee72aa00a70e9536dd2ae1900896c60f70150"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"1477ca8536702eacbd65a6a162cfe90d62016a14ffe58d52b7dd4c3628a27e5b",
+
+// 600c9816 diff 35
+"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe44000000000000000003bbb250f2dc23717e8192c0b8bec6a175cd059e4089d325006eaee3446254c9536dd39e1900896c16980c60"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"68db599d6b7a55fd61d4244a3dfa465055ead6b5c0a37c7a3d4555b58e99065e",
+
+// 70092d5f diff 114
+"000000023e91fce7300a792bfbaa0c76e1aa5f9b546c1db582aee4ff000000000000000072e17babd4089b204797cebda7dc6e277950eab1b2908991ae1d72335f82d204536dcf441900896c5f2d0970"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"c7d601ce3b01e569a49508d541bbcba9b3c8394b1834523ef1e5cb2c60bd34a3",
+
+// 800eeaa8 diff 159
+"000000029c6bf469abe4ad37605c097a860cff3cf5c1ef4377618f74000000000000000022388b6f022144db134af1bc8e61b385ca37cae038c1d165ae98c496b3b41e8b536dd2101900896ca8ea0e80"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"410761e97e67b494fd547cfe9ffbb36893da7aec75c6b51b8d5f38f87b5d63cf",
+
+// 900f600d diff 144
+"000000029c6bf469abe4ad37605c097a860cff3cf5c1ef4377618f7400000000000000000e1f0cfdf5ad8248fc4520f3bb0b2040226430348cddeff5ca9181beeb78870d536dd2161900896c0d600f90"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"ad1a8d354a7e8b13ec47f4c3d907d00945a61e86059f4943e42c1e52398eba5d",
+
+// a00210bf diff 1055
+"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe4400000000000000002232a16d38cc0e13e4b16d917bff4c34727deb3b5c50e424fb8453ff9b2adcb4536dd4231900896cbf1002a0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"adc67d38f31f589b18b9d8e531b994ce5733c021a03d88d38611ee6b4c2710a5",
+
+// b004309d diff 43
+"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe440000000000000000a2860471277b4a93fea2a8b6d8c281fab7bde3b78f2acd1bfdc89d464ed3bb3c536dd35e1900896c9d3004b0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"537686c611aae4397c7c04b2c190708453d00e8c9563525610c31ba46e80dbc2",
+
+// c00b7537 diff 64
+"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe440000000000000000f370230607998fbbd10275c5890885fcd81b68018ba2373abf0f93a06d02ab28536dd33e1900896c37750bc0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"1fdda952da6abd70022a6e5f2b9dc5e1b66011128c3fa249f0b7439f00d5943e",
+
+// d0005bd5 diff 1539
+"00000002c0a2c91fc41254539a5b2a27be28de2a6187e2af3f129d6300000000000000005e45ffc512d5ca3bc4d2063dd3af1669c296ae126a5a2ef896d1e190cedf67b9536dd46b1900896cd55b00d0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"07094d6cbe76538a88612624fc5e655cc405cb8198dcad516b88dbac5bf8b906",
+
+// e00a7796 diff 41
+"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe44000000000000000027c548815127c125147af91c356c293f0defbd2771f8dc3b1142b367528656db536dd37c1900896c96770ae0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"1bdbb3b1be7216872ea787627b03c389a527451f6dd832d8540874306f9c07c6",
+
+// f001f029 diff 77
+"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe440000000000000000adef758770bb90c5b13769c5b61affb322b24c747573b38ebe2ee81748d0b557536dd4071900896c29f001f0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"1ac8eea63285353944e40eec54d2dd6cd0994b447429bb0ed0598d38f42da0e2"
+};
+
+const char bench_lodiffs[16][324] = {
+// 000ed6b6 diff 2
+"00000002c01f502cb3e9fdb053230ec12a4954c1021a6b35862b5e29000000000000000084d1b83ae44057025e8c5b5756b44f04df5fffe4a7a30e5c12d12a97a7a4c2ea536dce431900896cb6d60e00"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"d08f7e14c50dad77dc238b4db2901a0578e657b1954779ab9cd82a73829edf7f",
+
+// 1000818f diff 5
+"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a44630000000000000000c1a174254a6593ffba987f68fe26e716e3c129a7f33a9c43ae7ecf90c8cd0d2c536dc4e61900896c8f810010"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"6700aeedada2b3877900b58a183c42c40949956bb8b4a8d21481f8936b572922",
+
+// 20006be9 diff 7
+"00000002138cf4b61dff74e3c26b2d80045064e8ab4802521bab2cda000000000000000071eef64a7ef4e47cda16e96673197d36c7235a4aadd23c21a38ce53827d1f8bc536dc4d71900896ce96b0020"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"0730cf7a6b8a85eb1cc017b109d23c392464f99aa8c020ea107c525b671adde0",
+
+// 300029f4 diff 2
+"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a44630000000000000000fe2c6b926468565e524ab7c2f111035dcde7c60955842111930589eccb410f83536dc66b1900896cf4290030"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"27dbb374a97f15c59587256662f36904d075d0e61f749618182711288ac617c7",
+
+// 40001d82 diff 2
+"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a4463000000000000000003073385e05c29f0435a6001c8eca9c8d5602890aeff9d4d103d3383cf80dae5536dc57c1900896c821d0040"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"7da3b97e82c0c3125a58dad8a0d1d0369244731f3b096e972484298d15b843d9",
+
+// 50003ce6 diff 1
+"000000029ca55e5f1bc0328c84f358fddadc13cb232599bc2ca9dbe10000000000000000b5b4d19c20a7fc2b174ff673c006edd2247c4b2336571864df93eb7ec0c8c276536dfe041900896ce63c0050"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"1514bd586511e531e2b6277a6d112b171f9e008d56ef4a971e619acf22e75072",
+
+// 60004314 diff 2
+"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a446300000000000000003e3030629ff4258056dc9efaf922bd173a65f65ee799b0c765097d3deeddef10536dc4d81900896c14430060"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"dcb77a9c36d894d2dbc31437e5c2a1564e927937848ea2eb20b38638afc64b96",
+
+// 700041d4 diff 7
+"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a446300000000000000005513c22bb99e9daa9936b0df5dce64d7737e3706be99e5098d112002492cf81b536dc5691900896cd4410070"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"0d896267fda5dda0f85308e77f754c8b94b7b88e3cb315475cd9efd16401e3ce",
+
+// 80009d99 diff 1
+"00000002e155f07e652e4d671ca4db51bbde14d2b5ae34ee67ecc74400000000000000004af5cffd7e5a7087f1b484b526c7350c86d8389283509ca878502f792115e8dc536dc6ad1900896c999d0080"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"339354568f506ac3cd69bb427b1af83a0473b87c16bf3b562a93d0a2ffc53e54",
+
+//  9000fb14 diff 4
+"000000029ca55e5f1bc0328c84f358fddadc13cb232599bc2ca9dbe100000000000000005925a624e5c84f96d2c34dce3b6a736addb891724b48a36320c7494435f9c915536dfe621900896c14fb0090"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"8362009c07cf48249f481be6b79e67247cab1d20050cf11c276085b90732110c",
+
+// a000eb5e diff 2
+"00000002e155f07e652e4d671ca4db51bbde14d2b5ae34ee67ecc74400000000000000001e69f1d6507f4b7b50980930f7d8089834fbe65f0980b8592d53cdda08e50d24536dc7da1900896c5eeb00a0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"21e4f654d26ab8c9164ff311657a9f9c4cdc0e8a09334925f7c02138819d7e61",
+
+// b0002ec5 diff 2
+"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a4463000000000000000064923b63f53c72c04ebe6c1c9140b6377132b6e50865814fe562291bd023d348536dc65a1900896cc52e00b0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"55db91a25401a89daf9ff7d7954bab722b894ba480fefaf1f0a95aaf5f600567",
+
+// c0001f6e diff 2
+"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a44630000000000000000ee9817160e35d4410601c8dc741c1a810c485f3b40a0859be5f58f0bf6ef1694536dc6321900896c6e1f00c0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"ae215785178ff6350064060ebbb219a71716a10e88528fc4bb1cb5c8fdd0cf60",
+
+// d0005f26 diff 7
+"000000029ca55e5f1bc0328c84f358fddadc13cb232599bc2ca9dbe100000000000000001e514cf738455a54f004ec86edafcfd9fd2022017bb31c245340353911744fb7536dfe1f1900896c265f00d0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"dcafaa86defe850b057ae74f7218a79b0ede086a196f18f0e7c585eb88d1139a",
+
+// e0008993 diff 2
+"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a446300000000000000005edbd53fcc64850b5334678199d769514818fbcc79861fc77e572bb4753b7fe2536dc5d91900896c938900e0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"5e653df5956ece518a78a5d11297431af94ce8ba91d80cfb2aa8c5b3095fa256",
+
+// f000709e diff 1
+"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a44630000000000000000596fc4aa5da839ba267c36aa1a5b29d813747b2273dc03aa9e404c4da0238e2b536dc4cc1900896c9e7000f0"
+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
+"0e23806a533bd956787eef52dd8edee456c60d6cecbb6175458ee53fc8c6c813"
+};
+#endif /* __BENCH_BLOCK_H__ */

+ 1230 - 0
bf16-bitfury16.c

@@ -0,0 +1,1230 @@
+#include <arpa/inet.h>
+
+#include "miner.h"
+
+#include "bf16-bitfury16.h"
+#include "bf16-communication.h"
+#include "bf16-ctrldevice.h"
+
+#include "math.h"
+#include "sha2.h"
+#include "driver-bitfury16.h"
+
+//#define FLIP_BITS
+
+bf_cmd_description_t cmd_description[CHIP_CMD_NUM] = {
+	{
+		.cmd_code        = CHIP_CMD_TASK_STATUS,
+		.cmd_description = "CHIP_CMD_TASK_STATUS"
+	},
+	{
+		.cmd_code        = CHIP_CMD_TASK_WRITE,
+		.cmd_description = "CHIP_CMD_TASK_WRITE"
+	},
+	{
+		.cmd_code        = CHIP_CMD_TASK_SWITCH,
+		.cmd_description = "CHIP_CMD_TASK_SWITCH"
+	},
+	{
+		.cmd_code        = CHIP_CMD_READ_NONCE,
+		.cmd_description = "CHIP_CMD_READ_NONCE"
+	},
+	{
+		.cmd_code        = CHIP_CMD_SET_CLOCK,
+		.cmd_description = "CHIP_CMD_SET_CLOCK"
+	},
+	{
+		.cmd_code        = CHIP_CMD_TOGGLE,
+		.cmd_description = "CHIP_CMD_TOGGLE"
+	},
+	{
+		.cmd_code        = CHIP_CMD_SET_MASK,
+		.cmd_description = "CHIP_CMD_SET_MASK"
+	},
+	{
+		.cmd_code        = CHIP_CMD_CREATE_CHANNEL,
+		.cmd_description = "CHIP_CMD_CREATE_CHANNEL"
+	}
+};
+
+char* get_cmd_description(bf_cmd_code_t cmd_code)
+{
+	uint8_t i;
+
+	for (i = 0; i < CHIP_CMD_NUM; i++)
+		if (cmd_description[i].cmd_code == cmd_code)
+			return cmd_description[i].cmd_description;
+
+	return NULL;
+}
+
+static void shift_bits(uint8_t* data, uint8_t size, uint8_t nbits)
+{
+	uint8_t i;
+	uint8_t bytes = nbits / 8;
+	uint8_t bits  = nbits % 8;
+
+	for (i = 0; i < size; i++) {
+		data[i] = (data[i + bytes] << bits);
+		uint8_t minor = (data[i + bytes + 1] >> (8 - bits));
+		data[i] |= minor;
+	}
+}
+
+static uint8_t extra_bytes(uint8_t depth)
+{
+	return (depth * 3) / 8 + 1;
+}
+
+static uint8_t analyze_rx_data(bf_command_t* command, bf_cmd_status_t* cmd_status, uint32_t* nonces)
+{
+	uint8_t i;
+	uint8_t res = 0;
+
+	if (command->cmd_code & CHIP_CMD_READ_NONCE) {
+		shift_bits(command->rx, 49 + 2 + extra_bytes(command->depth), command->depth * 3);
+
+		command->status	   = command->rx[0];
+		cmd_status->status = command->rx[0];
+		command->checksum             = command->rx[1];
+		cmd_status->checksum_expected = command->checksum;
+
+		if (nonces == NULL)
+			return 1;
+
+		uint32_t nonce = 0x00000000;
+
+		/* fill nonces buffer */
+		for (i = 0; i <= 48; i++) {
+			if ((i % 4 == 0) && (i != 0)) {
+				if ((nonce & 0x0fffffff) != 0x0fffffff)
+					nonce ^= 0xaaaaaaaa;
+				nonces[i/4 - 1] = nonce;
+
+				if (i == 48)
+					break;
+
+				nonce = 0x00000000;
+			}
+
+			nonce |= (command->rx[i + 2] << 8*(4 - (i%4) - 1));
+		}
+
+#if 0
+		char data[128];
+		memset(data, 0, sizeof(data));
+		for (i = 0; i < 49 + 2 + extra_bytes(command->depth); i++)
+			sprintf(data, "%s%02x", data, command->rx[i]);
+		applog(LOG_DEBUG, "BF16: RX <- [%s]", data);
+#endif
+
+		for (i = 0; i < 48; i ++)
+			command->nonce_checksum += command->rx[i + 2];
+		command->nonce_checksum += command->rx[1];
+
+		cmd_status->nonce_checksum_expected = command->nonce_checksum;
+
+		if (command->checksum != command->rx[1]) {
+			command->checksum_error       = true;
+			cmd_status->checksum_error    = true;
+			cmd_status->checksum_received = command->rx[50];
+			res = 1;
+#if 0
+			applog(LOG_ERR, "Checksum mismatch: received [%02x] expected [%02x]", command->rx[1], 0x04);
+#endif
+		} else {
+			command->checksum_error    = false;
+			cmd_status->checksum_error = false;
+		}
+
+		if ((command->nonce_checksum != command->rx[50]) &&
+			(command->nonce_checksum != command->rx[50] + 1)) {
+			command->nonce_checksum_error       = true;
+			cmd_status->nonce_checksum_error    = true;
+			cmd_status->nonce_checksum_received = command->rx[50];
+
+			res += 2;
+#if 0
+			applog(LOG_ERR, "Nonce checksum mismatch: received [%02x] expected [%02x]", command->rx[50], checksum);
+#endif
+		} else {
+			command->nonce_checksum_error    = false;
+			cmd_status->nonce_checksum_error = false;
+		}
+	} else {
+		shift_bits(command->rx, 2 + extra_bytes(command->depth), command->depth * 3);
+
+		if (opt_bf16_test_chip != NULL) {
+			char data[16];
+			memset(data, 0, sizeof(data));
+			for (i = 0; i < 2 + extra_bytes(command->depth); i++)
+				sprintf(data, "%s%02x", data, command->rx[i]);
+			applog(LOG_NOTICE, "BF16: RX <- [%s]", data);
+		}
+
+		command->status	     = command->rx[0];
+		cmd_status->status   = command->rx[0];
+		uint8_t cmd_checksum = command->rx[1];
+		cmd_status->checksum_expected = command->checksum;
+
+#if 0
+		applog(LOG_DEBUG, "Command checksum: [%02x]", cmd_checksum);
+		applog(LOG_DEBUG, "Command status:   [%02x]", command->status);
+#endif
+
+		if ((command->checksum != cmd_checksum) &&
+		    (command->checksum != cmd_checksum + 1)) {
+			command->checksum_error       = true;
+			cmd_status->checksum_error    = true;
+			cmd_status->checksum_received = cmd_checksum;
+			res = 1;
+
+			if (opt_bf16_test_chip != NULL) {
+				applog(LOG_ERR, "BF16: checksum mismatch: received [%02x] expected [%02x]",
+						cmd_checksum, command->checksum);
+			}
+		} else {
+			command->checksum_error    = false;
+			cmd_status->checksum_error = false;
+		}
+	}
+
+	return res;
+}
+
+/* SPI BTC250 primitives */
+uint8_t create_channel(spi_channel_id_t spi_channel, uint8_t* channel_path, uint8_t channel_length)
+{
+	int res = 0;
+
+	uint8_t* channel_path_buff = cgcalloc(channel_length, sizeof(uint8_t));
+	cg_memcpy(channel_path_buff, channel_path, channel_length);
+
+	res = device_spi_transfer(spi_channel, channel_path_buff, channel_length);
+
+	free(channel_path_buff);
+
+	return res;
+}
+
+uint8_t destroy_channel(spi_channel_id_t spi_channel, uint8_t depth)
+{
+	bf_command_t chip_command;
+	bf_chip_address_t chip_address = { 0x00, 0x00, 0x0f };
+
+	/* send command to 0x0f address */
+	spi_command_init(&chip_command, depth, chip_address, CHIP_CMD_TASK_SWITCH, 0, NULL);
+	return spi_command_exec(spi_channel, &chip_command, NULL);
+}
+
+/* SPI BTC16 primitives  */
+void spi_emit_reset(spi_channel_id_t spi_channel)
+{
+	device_ctrl_transfer(spi_channel, 1, F_RST);
+
+	uint8_t data[2] = { 0x00, 0x00 };
+	device_spi_transfer(spi_channel, data, sizeof(data));
+
+	device_ctrl_transfer(spi_channel, 0, F_RST);
+}
+
+uint8_t send_toggle(spi_channel_id_t spi_channel, uint8_t depth, bf_chip_address_t chip_address)
+{
+	bf_command_t chip_command;
+	uint8_t toggle[4] = { 0xa5, 0x00, 0x00, 0x02 };
+
+	spi_command_init(&chip_command, depth, chip_address, CHIP_CMD_TOGGLE, 3, toggle);
+	return spi_command_exec(spi_channel, &chip_command, NULL);
+}
+
+uint8_t set_clock(spi_channel_id_t spi_channel, uint8_t depth,
+		bf_chip_address_t chip_address, uint8_t clock)
+{
+	bf_command_t chip_command;
+	uint8_t clock_buf[4];
+
+	memset(clock_buf, 0, 4);
+	gen_clock_data(clock, 1, clock_buf);
+	spi_command_init(&chip_command, depth, chip_address, CHIP_CMD_SET_CLOCK, 3, clock_buf);
+	return spi_command_exec(spi_channel, &chip_command, NULL);
+}
+
+/* cmd buffer primitives */
+int8_t cmd_buffer_init(bf_cmd_buffer_t* cmd_buffer)
+{
+	if (cmd_buffer == NULL)
+		return -1;
+
+	cmd_buffer->cmd_list = cgmalloc(sizeof(bf_list_t));
+	cmd_buffer->cmd_list->head  = NULL;
+	cmd_buffer->cmd_list->tail  = NULL;
+	cmd_buffer->cmd_list->count = 0;
+
+	cmd_buffer->tx_buffer = cgcalloc(CMD_BUFFER_LEN, sizeof(uint8_t));
+	cmd_buffer->rx_buffer = cgcalloc(CMD_BUFFER_LEN, sizeof(uint8_t));
+
+	memset(cmd_buffer->tx_buffer, 0, CMD_BUFFER_LEN);
+	memset(cmd_buffer->rx_buffer, 0, CMD_BUFFER_LEN);
+
+	cmd_buffer->free_bytes = CMD_BUFFER_LEN;
+	cmd_buffer->tx_offset  = 0;
+	cmd_buffer->rx_offset  = 0;
+	cmd_buffer->status     = EMPTY;
+
+	return 0;
+}
+
+int8_t cmd_buffer_deinit(bf_cmd_buffer_t* cmd_buffer)
+{
+	if (cmd_buffer == NULL)
+		return -1;
+
+	/* free cmd buffer */
+	while (cmd_buffer->cmd_list->head != NULL) {
+		bf_data_t* cdata = cmd_buffer->cmd_list->head;
+		LIST_POP_HEAD(cmd_buffer->cmd_list);
+		free(cdata->data);
+		free(cdata);
+	}
+
+	free(cmd_buffer->cmd_list);
+
+	/* free RX/TX buffer */
+	free(cmd_buffer->tx_buffer);
+	free(cmd_buffer->rx_buffer);
+
+	cmd_buffer->free_bytes = CMD_BUFFER_LEN;
+	cmd_buffer->tx_offset  = 0;
+	cmd_buffer->rx_offset  = 0;
+	cmd_buffer->status     = EMPTY;
+
+	return 0;
+}
+
+int8_t cmd_buffer_clear(bf_cmd_buffer_t* cmd_buffer)
+{
+	if (cmd_buffer == NULL)
+		return -1;
+
+	/* release cmd buffer data memory */
+	while (cmd_buffer->cmd_list->head != NULL) {
+		bf_data_t* cdata = cmd_buffer->cmd_list->head;
+		LIST_POP_HEAD(cmd_buffer->cmd_list);
+		free(cdata->data);
+		free(cdata);
+	}
+
+	cmd_buffer->cmd_list->count = 0;
+
+	/* clear RX/TX buffer */
+	memset(cmd_buffer->tx_buffer, 0, CMD_BUFFER_LEN);
+	memset(cmd_buffer->rx_buffer, 0, CMD_BUFFER_LEN);
+
+	cmd_buffer->free_bytes = CMD_BUFFER_LEN;
+	cmd_buffer->tx_offset  = 0;
+	cmd_buffer->rx_offset  = 0;
+	cmd_buffer->status     = EMPTY;
+
+	return 0;
+}
+
+int8_t cmd_buffer_push(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth,
+		const bf_chip_address_t chip_address, const bf_chip_address_t src_address,
+		const bf_works_t work, const uint32_t id,
+		const bf_cmd_code_t cmd_code, const uint8_t data_length, const uint8_t* tx)
+{
+	uint8_t res = 0;
+
+	if (cmd_buffer == NULL)
+		return -1;
+
+	if (cmd_buffer->status == EXECUTED)
+		return -2;
+
+	bf_command_t command;
+	memset(&command, 0, sizeof(bf_command_t));
+
+	uint8_t buff[192];
+	memset(buff, 0, sizeof(buff));
+
+	if (cmd_code != CHIP_CMD_CREATE_CHANNEL) {
+		res = spi_command_init(&command, depth, chip_address, cmd_code, data_length, tx);
+		if (res != 0)
+			return res;
+	}
+
+	/* init structure */
+	bf_data_t* cdata = cgmalloc(sizeof(bf_data_t));
+	cdata->data = cgmalloc(sizeof(bf_cmd_t));
+	cdata->next = NULL;
+	cdata->prev = NULL;
+
+	cg_memcpy(&CMD(cdata)->chip_address, &chip_address, sizeof(bf_chip_address_t));
+	cg_memcpy(&CMD(cdata)->src_address,  &src_address,  sizeof(bf_chip_address_t));
+	cg_memcpy(&CMD(cdata)->work,         &work,         sizeof(bf_works_t));
+	CMD(cdata)->id       = id;
+	CMD(cdata)->depth    = command.depth;
+	CMD(cdata)->checksum = command.checksum;
+	CMD(cdata)->cmd_code = cmd_code;
+
+	if (CMD(cdata)->cmd_code & CHIP_CMD_READ_NONCE)
+		CMD(cdata)->data_length = command.data_length + 49 + 2 + extra_bytes(command.depth);
+	else if (CMD(cdata)->cmd_code == CHIP_CMD_CREATE_CHANNEL)
+		CMD(cdata)->data_length = data_length;
+	else
+		CMD(cdata)->data_length = command.data_length + 2 + extra_bytes(command.depth);
+
+	if (cmd_buffer->free_bytes < CMD(cdata)->data_length) {
+		/* not enough TX/RX buffer space available */
+		free(cdata->data);
+		free(cdata);
+		return -3;
+	}
+
+	/* init send buffer */
+	if (cmd_code != CHIP_CMD_CREATE_CHANNEL) {
+		cg_memcpy(buff, command.tx, command.data_length);
+		cg_memcpy(cmd_buffer->tx_buffer + cmd_buffer->tx_offset, buff, CMD(cdata)->data_length);
+	} else
+		cg_memcpy(cmd_buffer->tx_buffer + cmd_buffer->tx_offset, tx, CMD(cdata)->data_length);
+
+#if 0
+	uint16_t i;
+	char data[384];
+	memset(data, 0, sizeof(data));
+	for (i = 0; i < command.data_length; i++)
+		sprintf(data, "%s%02x", data, command.tx[i]);
+	applog(LOG_DEBUG, "BF16: TX -> [%s]", data);
+#endif
+
+	cmd_buffer->tx_offset  += CMD(cdata)->data_length;
+	cmd_buffer->free_bytes -= CMD(cdata)->data_length;
+
+	/* add cmd to buffer */
+	LIST_PUSH_TAIL(cmd_buffer->cmd_list, cdata);	
+	cmd_buffer->cmd_list->count++;
+
+	return 0;
+}
+
+int8_t cmd_buffer_push_send_toggle(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth,
+		const bf_chip_address_t chip_address)
+{
+	bf_works_t work;
+	uint8_t toggle[4] = { 0xa5, 0x00, 0x00, 0x02 };
+
+	return cmd_buffer_push(cmd_buffer, depth, chip_address, chip_address,
+			work, 0, CHIP_CMD_TOGGLE, 3, toggle);
+}
+
+int8_t cmd_buffer_push_set_clock(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth,
+		const bf_chip_address_t chip_address, uint8_t clock)
+{
+	bf_works_t work;
+	uint8_t clock_buf[4];
+
+	memset(clock_buf, 0, 4);
+	gen_clock_data(clock, 1, clock_buf);
+
+	return cmd_buffer_push(cmd_buffer, depth, chip_address, chip_address,
+			work, 0, CHIP_CMD_SET_CLOCK, 3, clock_buf);
+}
+
+int8_t cmd_buffer_push_set_mask(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth,
+		const bf_chip_address_t chip_address, uint8_t mask)
+{
+	uint8_t i;
+	bf_works_t work;
+	uint8_t noncemask[4];
+
+	for (i = 0; i < 4; i++)
+		noncemask[i] = (mask >> (8*(4 - i - 1))) & 0xff;
+
+	return cmd_buffer_push(cmd_buffer, depth, chip_address, chip_address,
+			work, 0, CHIP_CMD_SET_MASK, 3, noncemask);
+}
+
+int8_t cmd_buffer_push_create_channel(bf_cmd_buffer_t* cmd_buffer,
+		uint8_t* channel_path, uint8_t channel_length)
+{
+	bf_works_t work;
+	bf_chip_address_t chip_address = { 0x00, 0x00, 0x00 };
+
+	return cmd_buffer_push(cmd_buffer, 0, chip_address, chip_address,
+			work, 0, CHIP_CMD_CREATE_CHANNEL, channel_length, channel_path);
+}
+
+int8_t cmd_buffer_push_destroy_channel(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth)
+{
+	bf_works_t work;
+	bf_chip_address_t chip_address = { 0x00, 0x00, 0x0f };
+
+	return cmd_buffer_push(cmd_buffer, depth, chip_address, chip_address,
+			work, 0, CHIP_CMD_TASK_SWITCH, 0, NULL);
+}
+
+bool match_nonce(uint32_t nonce, uint32_t mask, uint8_t nbits)
+{
+	uint32_t fixed_mask = (uint32_t)(pow(2, nbits) - 1);
+	return ((nonce & fixed_mask) == (mask & fixed_mask));
+}
+
+uint8_t find_nonces(uint32_t* curr_nonces, uint32_t* prev_nonces, uint32_t* valid_nonces)
+{
+	uint8_t i, j;
+	uint8_t found  = 0;
+	uint8_t nonces = 0;
+	uint8_t diff = 0;
+
+	uint32_t found_nonces[12];
+	memset(found_nonces, 0, sizeof(found_nonces));
+
+	for (i = 0; i < 12; i++) {
+		if (((curr_nonces[i] & 0x0fffffff) != 0x0fffffff) &&
+			(curr_nonces[i] != prev_nonces[i])) {
+			found_nonces[found++] = curr_nonces[i];
+		}
+
+		if (((curr_nonces[i] & 0x0fffffff) == 0x0fffffff) &&
+			(curr_nonces[i] != prev_nonces[i]))
+			diff++;
+	}
+
+	for (i = 0; i < found; i++) {
+		if (found_nonces[i] == 0x00000000)
+			continue;
+
+		for (j = i; j < found; j++) {
+			if ((j != i) && (found_nonces[i] == found_nonces[j]))
+				found_nonces[j] = 0x00000000;
+		}
+
+		valid_nonces[nonces++] = found_nonces[i];
+	}
+
+	return nonces;
+}
+
+int8_t cmd_buffer_pop(bf_cmd_buffer_t* cmd_buffer, bf_cmd_status_t* cmd_status, uint32_t* nonces)
+{
+	if (cmd_buffer == NULL)
+		return -1;
+
+	if (cmd_buffer->status != EXECUTED)
+		return -2;
+
+	if (cmd_buffer->cmd_list->head == NULL)
+		return -3;
+
+	uint8_t buff[192];
+	bf_command_t chip_command;
+	memset(buff, 0, sizeof(buff));
+	memset(chip_command.rx, 0, sizeof(chip_command.rx));
+
+	/* extract command from list */
+	bf_data_t* cdata = cmd_buffer->cmd_list->head;
+
+	chip_command.cmd_code             = CMD(cdata)->cmd_code;
+	chip_command.depth                = CMD(cdata)->depth;
+	chip_command.data_length          = CMD(cdata)->data_length;
+	chip_command.status               = 0;
+	chip_command.checksum             = CMD(cdata)->checksum;
+	chip_command.nonce_checksum       = 0;
+	chip_command.checksum_error       = false;
+	chip_command.nonce_checksum_error = false;
+
+	/* extract chip return data */
+	cg_memcpy(buff, cmd_buffer->rx_buffer + cmd_buffer->rx_offset, CMD(cdata)->data_length);
+	cmd_buffer->rx_offset += CMD(cdata)->data_length;
+	if (CMD(cdata)->cmd_code & CHIP_CMD_READ_NONCE) {
+		cg_memcpy(chip_command.rx, buff + CMD(cdata)->data_length - (49 + 2 + extra_bytes(chip_command.depth)),
+				49 + 2 + extra_bytes(chip_command.depth));
+		memset(nonces, 0, 12 * sizeof(uint32_t));
+		analyze_rx_data(&chip_command, cmd_status, nonces);
+	} else if (CMD(cdata)->cmd_code == CHIP_CMD_CREATE_CHANNEL) {
+		cg_memcpy(chip_command.rx, buff, CMD(cdata)->data_length);
+	} else {
+		cg_memcpy(chip_command.rx, buff + CMD(cdata)->data_length - (2 + extra_bytes(chip_command.depth)),
+				2 + extra_bytes(chip_command.depth));
+		analyze_rx_data(&chip_command, cmd_status, NULL);
+	}
+
+	/* prepare cmd_status */
+	cg_memcpy(&cmd_status->chip_address, &CMD(cdata)->chip_address, sizeof(bf_chip_address_t));
+	cg_memcpy(&cmd_status->src_address,  &CMD(cdata)->src_address,  sizeof(bf_chip_address_t));
+	cg_memcpy(&cmd_status->work,         &CMD(cdata)->work,         sizeof(bf_works_t));
+	cmd_status->id       = CMD(cdata)->id;
+	cmd_status->cmd_code = CMD(cdata)->cmd_code;
+
+	/* push memory back to free cmd list */
+	LIST_POP_HEAD(cmd_buffer->cmd_list);
+	cmd_buffer->cmd_list->count--;
+	free(cdata->data);
+	free(cdata);
+
+	return 0;
+}
+
+int8_t cmd_buffer_exec(spi_channel_id_t spi_channel, bf_cmd_buffer_t* cmd_buffer)
+{
+	if (cmd_buffer == NULL)
+		return -1;
+
+	if (cmd_buffer->status == TX_READY) {
+		device_spi_txrx(spi_channel, cmd_buffer->tx_buffer, cmd_buffer->rx_buffer, cmd_buffer->tx_offset);
+		cmd_buffer->status = EXECUTED;
+	} else
+		return -2;
+
+	return 0;
+}
+
+/* BF16 command primitives */
+uint8_t gen_clock_data(uint8_t clock, uint8_t prescaler, uint8_t data[4])
+{
+	uint8_t i;
+	uint32_t data32 = 0x00000000;
+
+	if (clock > 0x3f)
+		return -1;
+
+	if ((prescaler != 0) && (prescaler != 1))
+		return -1;
+
+	uint32_t magic_const = 0x38;
+	uint32_t prescaler1 = prescaler;
+	uint32_t clock1 = clock;
+	uint32_t prescaler2 = prescaler;
+	uint32_t clock2 = clock;
+
+	magic_const <<= 20;
+
+	if (prescaler == 1) {
+		prescaler1 <<= 19;
+		prescaler2 <<= 12;
+	}
+
+	clock1 <<= 13;
+	clock2 <<= 6;
+
+	data32 = magic_const | prescaler1 | clock1 | prescaler2 | clock2;
+	for (i = 0; i < 4; i++)
+		data[i] = (data32 >> (8*(4 - i - 1))) & 0xff;
+
+	return 0;
+}
+
+#ifdef FLIP_BITS
+static uint32_t flip_bits(uint32_t data, uint8_t nbits)
+{
+	uint32_t ret = 0x00000000;
+	uint8_t i;
+
+	for (i = 0; i < nbits; i++)
+		ret |= (((data >> i) & 0x1) << (nbits - (i + 1)));
+
+	return ret;
+}
+#endif
+
+uint32_t gen_mask(uint32_t nonce, uint8_t nbits)
+{
+	uint32_t mask = 0x00000000;
+
+	uint32_t mask_code = (nbits << 16);
+
+	/* highest 16 bits of nonce counter */
+	uint32_t nonce_code = (nonce & 0x0007ffff);
+#ifdef FLIP_BITS
+	uint32_t nonce_cntr = flip_bits(nonce_code, 19);
+	nonce_code = (flip_bits(nonce_cntr - 2, 19) ^ 0xaaaaaaaa);
+	mask = (mask_code | (nonce_code & (uint32_t)(pow(2, nbits) - 1)));
+#else
+	nonce_code = ((nonce_code ^ 0xaaaaaaaa) & (uint32_t)(pow(2, nbits) - 1));
+	mask = (mask_code | nonce_code);
+#endif
+
+	return mask;
+}
+
+void ms3steps16(uint32_t* p, uint32_t* w, uint32_t* task)
+{
+	uint32_t a, b, c, d, e, f, g, h, new_e, new_a;
+	uint8_t i;
+
+	a = p[0];
+	b = p[1];
+	c = p[2];
+	d = p[3];
+	e = p[4];
+	f = p[5];
+	g = p[6];
+	h = p[7];
+	for (i = 0; i < 3; i++) {
+		new_e = w[i] + sha256_k[i] + h + CH(e,f,g) + SHA256_F2(e) + d;
+		new_a = w[i] + sha256_k[i] + h + CH(e,f,g) + SHA256_F2(e) +
+		        SHA256_F1(a) + MAJ(a,b,c);
+		d = c;
+		c = b;
+		b = a;
+		a = new_a;
+		h = g;
+		g = f;
+		f = e;
+		e = new_e;
+	}
+
+	task[18] = ntohl(a ^ 0xaaaaaaaa);
+	task[17] = ntohl(b ^ 0xaaaaaaaa);
+	task[16] = ntohl(c ^ 0xaaaaaaaa);
+	task[15] = ntohl(d ^ 0xaaaaaaaa);
+	task[11] = ntohl(e ^ 0xaaaaaaaa);
+	task[10] = ntohl(f ^ 0xaaaaaaaa);
+	task[9]  = ntohl(g ^ 0xaaaaaaaa);
+	task[8]  = ntohl(h ^ 0xaaaaaaaa);
+}
+
+uint8_t gen_task_data(uint32_t* midstate, uint32_t merkle, uint32_t ntime,
+		uint32_t nbits, uint32_t mask, uint8_t* task)
+{
+	uint8_t  i;
+	uint32_t tmp;
+	uint32_t w[3];
+
+	w[0] = merkle;
+	w[1] = ntime;
+	w[2] = nbits;
+
+	for (i = 0; i < 8; i++) {
+		tmp = midstate[i];
+		tmp ^= 0xaaaaaaaa;
+		tmp = ntohl(tmp);
+		cg_memcpy(task + i*4, &tmp, sizeof(tmp));
+	}
+
+	ms3steps16(midstate, w, (uint32_t*)task);
+
+	for (i = 0; i < 3; i++) {
+		tmp = w[i];
+		tmp ^= 0xaaaaaaaa;
+		tmp = ntohl(tmp);
+		cg_memcpy(task + (12 + i)*4, &tmp, sizeof(tmp));
+	}
+
+	mask = ntohl(mask);
+	cg_memcpy(task + 19*4, &mask, sizeof(mask));
+
+	return 0;
+}
+
+uint8_t spi_command_init(bf_command_t* command, const uint8_t depth,
+		const bf_chip_address_t chip_address, const bf_cmd_code_t cmd_code,
+		const uint8_t data_length, const uint8_t* tx)
+{
+	uint8_t i;
+
+	memset(command->tx, 0, sizeof(command->tx));
+	memset(command->rx, 0, sizeof(command->rx));
+
+	command->tx[0] = 0x01;
+	command->data_length = 1;
+
+	if ((chip_address.chip_id > 10) && (chip_address.chip_id != 0x0f))
+		return 1;
+	else {
+		cg_memcpy(&command->chip_address, &chip_address, sizeof(bf_chip_address_t));
+		command->tx[1] = (command->chip_address.chip_id << 4);
+		command->data_length++;
+	}
+
+	command->depth = depth;
+
+	command->cmd_code = cmd_code;
+	command->tx[2] = command->cmd_code;
+	command->data_length++;
+
+	if (data_length <= 79) {
+		command->tx[3] = data_length;
+		command->data_length++;
+	} else
+		return 1;
+
+	/* fill TX data */
+	if (data_length == 0) {
+		command->tx[4] = 0x00;
+		command->data_length++;
+	} else if (tx != NULL) {
+		cg_memcpy(command->tx + 4, tx, data_length + 1);
+		command->data_length += (data_length + 1);
+	} else
+		return 1;
+
+	/* calculate checksum */
+	command->checksum = 0;
+	command->nonce_checksum = 0;
+	for (i = 2; i < command->data_length; i++)
+		command->checksum += command->tx[i];
+
+	command->checksum_error       = false;
+	command->nonce_checksum_error = false;
+
+	return 0;
+}
+
+uint8_t spi_command_exec(spi_channel_id_t spi_channel, bf_command_t* command, uint32_t* nonces)
+{
+	uint8_t buff[192];
+	uint8_t res = 0;
+	bf_cmd_status_t cmd_status;
+
+	if (command->cmd_code & CHIP_CMD_READ_NONCE) {
+		memset(buff, 0x00, command->data_length + 49 + 2 + extra_bytes(command->depth));
+		cg_memcpy(buff, command->tx, command->data_length);
+		device_spi_transfer(spi_channel, buff, command->data_length + 49 + 2 + extra_bytes(command->depth));
+		cg_memcpy(command->rx, buff + command->data_length, 49 + 2 + extra_bytes(command->depth));
+
+#if 0
+		uint16_t i;
+		char data[256];
+		memset(data, 0, sizeof(data));
+		for (i = 0; i < command->data_length; i++)
+			sprintf(data, "%s%02x", data, command->tx[i]);
+		applog(LOG_DEBUG, "BF16: TX -> [%s]", data);
+#endif
+
+		return analyze_rx_data(command, &cmd_status, nonces);
+	} else {
+		memset(buff, 0x00, command->data_length + 2 + extra_bytes(command->depth));
+		cg_memcpy(buff, command->tx, command->data_length);
+		device_spi_transfer(spi_channel, buff, command->data_length + 2 + extra_bytes(command->depth));
+		cg_memcpy(command->rx, buff + command->data_length, 2 + extra_bytes(command->depth));
+
+		if (opt_bf16_test_chip != NULL) {
+			uint16_t i;
+			char data[256];
+			memset(data, 0, sizeof(data));
+			for (i = 0; i < command->data_length; i++)
+				sprintf(data, "%s%02x", data, command->tx[i]);
+			applog(LOG_NOTICE, "BF16: TX -> [%s]", data);
+		}
+
+		return analyze_rx_data(command, &cmd_status, nonces);
+	}
+
+	return res;
+}
+
+/* dynamic work list primitives */
+bf_list_t* workd_list_init(void)
+{
+	bf_list_t* list = cgmalloc(sizeof(bf_list_t));
+	list->head = NULL;
+	list->tail = NULL;
+	list->count = 0;
+	pthread_mutex_init(&list->lock, NULL);
+
+	return list;
+}
+
+int8_t workd_list_deinit(bf_list_t* list, struct cgpu_info *bitfury)
+{
+	if (list == NULL)
+		return -1;
+
+	/* free work list */
+	L_LOCK(list);
+	while (list->head != NULL) {
+		bf_data_t* wdata = list->head;
+		LIST_POP_HEAD(list);
+
+		if (WORKD(wdata)->rolled)
+			free_work(WORKD(wdata)->work);
+		else
+			work_completed(bitfury, WORKD(wdata)->work);
+
+		free(wdata);
+	}
+	L_UNLOCK(list);
+
+	pthread_mutex_destroy(&list->lock);
+	list->count = 0;
+	free(list);
+
+	return 0;
+}
+
+int8_t workd_list_push(bf_list_t* list, bf_workd_t* work)
+{
+	if ((list == NULL) || (work == NULL))
+		return -1;
+
+	bf_data_t* wdata = cgmalloc(sizeof(bf_data_t));
+	wdata->data = work;
+	wdata->next = NULL;
+	wdata->prev = NULL;
+
+	LIST_PUSH_TAIL(list, wdata);
+	list->count++;
+
+	return 0;
+}
+
+int8_t workd_list_pop(bf_list_t* list, struct cgpu_info *bitfury)
+{
+	if (list == NULL)
+		return -1;
+
+	bf_data_t* wdata = list->head;
+	if (wdata != NULL) {
+		LIST_POP_HEAD(list);	
+
+		if (WORKD(wdata)->rolled)
+			free_work(WORKD(wdata)->work);
+		else
+			work_completed(bitfury, WORKD(wdata)->work);
+
+		list->count--;
+		free(wdata->data);
+		free(wdata);
+	} else
+		return -1;
+
+	return 0;
+}
+
+int8_t workd_list_remove(bf_list_t* list, bf_works_t* works)
+{
+	if (list == NULL)
+		return -1;
+
+	bf_data_t* wdata = list->head;
+	if (wdata != NULL) {
+		LIST_POP_HEAD(list);	
+
+		cg_memcpy(&works->work,    WORKD(wdata)->work,     sizeof(struct work));
+		cg_memcpy(&works->payload, &WORKD(wdata)->payload, sizeof(bf_payload_t));
+
+		list->count--;
+		free(wdata);
+	} else
+		return -1;
+
+	return 0;
+}
+
+/* nonces list primitives */
+bf_list_t* nonce_list_init(void)
+{
+	bf_list_t* list = cgmalloc(sizeof(bf_list_t));
+	list->head = NULL;
+	list->tail = NULL;
+	list->count = 0;
+	pthread_mutex_init(&list->lock, NULL);
+
+	return list;
+}
+
+int8_t nonce_list_deinit(bf_list_t* list)
+{
+	if (list == NULL)
+		return -1;
+
+	/* free nonce list */
+	L_LOCK(list);
+	while (list->head != NULL) {
+		bf_data_t* ndata = list->head;
+		LIST_POP_HEAD(list);
+		free(ndata->data);
+		free(ndata);
+	}
+	L_UNLOCK(list);
+
+	pthread_mutex_destroy(&list->lock);
+	list->count = 0;
+	free(list);
+	
+	return 0;
+}
+
+int8_t nonce_list_push(bf_list_t* list, uint32_t nonce)
+{
+	if (list == NULL)
+		return -1;
+
+	/* find nonce duplicates */
+	bf_data_t* ndata = list->head;
+	while (ndata != NULL) {
+		if (NONCE(ndata)->nonce == nonce)
+			return -1;
+		ndata = ndata->next;
+	}
+
+	ndata = cgmalloc(sizeof(bf_data_t));
+	ndata->data = cgmalloc(sizeof(bf_nonce_t));
+	NONCE(ndata)->nonce = nonce;
+	ndata->next = NULL;
+	ndata->prev = NULL;
+
+	LIST_PUSH_TAIL(list, ndata);
+	list->count++;
+
+	return 0;
+}
+
+uint32_t nonce_list_pop(bf_list_t* list)
+{
+	uint32_t nonce = 0;
+
+	bf_data_t* ndata = list->head;
+	if (ndata != NULL) {
+		nonce = NONCE(ndata)->nonce;
+		LIST_POP_HEAD(list);	
+		list->count--;
+		free(ndata->data);
+		free(ndata);
+	} else
+		return -1;
+
+	return nonce;
+}
+
+/* renoncework list primitives */
+bf_list_t* renoncework_list_init(void)
+{
+	bf_list_t* list = cgmalloc(sizeof(bf_list_t));
+	list->head = NULL;
+	list->tail = NULL;
+	list->count = 0;
+	pthread_mutex_init(&list->lock, NULL);
+
+	return list;
+}
+
+int8_t renoncework_list_deinit(bf_list_t* list)
+{
+	if (list == NULL)
+		return -1;
+
+	/* free renoncework list */
+	L_LOCK(list);
+	while (list->head != NULL) {
+		bf_data_t* rnwdata = list->head;
+		LIST_POP_HEAD(list);
+
+		free(rnwdata->data);
+		free(rnwdata);
+	}
+	L_UNLOCK(list);
+
+	pthread_mutex_destroy(&list->lock);
+	list->count = 0;
+	free(list);
+
+	return 0;
+}
+
+int8_t renoncework_list_push(bf_list_t* list, bf_chip_address_t src_address, uint32_t nonce)
+{
+	if (list == NULL)
+		return -1;
+
+	bf_data_t* rnwdata = cgmalloc(sizeof(bf_data_t));
+	rnwdata->data = cgmalloc(sizeof(bf_renoncework_t));
+	rnwdata->next = NULL;
+	rnwdata->prev = NULL;
+
+	cg_memcpy(&RENONCEWORK(rnwdata)->src_address, &src_address, sizeof(bf_chip_address_t));
+	RENONCEWORK(rnwdata)->nonce = nonce;
+
+	LIST_PUSH_TAIL(list, rnwdata);
+	list->count++;
+
+	return 0;
+}
+
+int8_t renoncework_list_pop(bf_list_t* list)
+{
+	if (list == NULL)
+		return -1;
+
+	bf_data_t* rnwdata = list->head;
+	if (rnwdata != NULL) {
+		LIST_POP_HEAD(list);	
+		list->count--;
+		free(rnwdata->data);
+		free(rnwdata);
+	} else
+		return -1;
+
+	return 0;
+}
+
+/* noncework list primitives */
+bf_list_t* noncework_list_init(void)
+{
+	bf_list_t* list = cgmalloc(sizeof(bf_list_t));
+	list->head = NULL;
+	list->tail = NULL;
+	list->count = 0;
+	pthread_mutex_init(&list->lock, NULL);
+
+	return list;
+}
+
+int8_t noncework_list_deinit(bf_list_t* list)
+{
+	if (list == NULL)
+		return -1;
+
+	/* free noncework list */
+	L_LOCK(list);
+	while (list->head != NULL) {
+		bf_data_t* nwdata = list->head;
+		LIST_POP_HEAD(list);
+
+		free(nwdata->data);
+		free(nwdata);
+	}
+	L_UNLOCK(list);
+
+	pthread_mutex_destroy(&list->lock);
+	list->count = 0;
+	free(list);
+
+	return 0;
+}
+
+int8_t noncework_list_push(bf_list_t* list, bf_chip_address_t chip_address,
+		bf_chip_address_t src_address, bf_works_t cwork, bf_works_t owork, uint32_t nonce)
+{
+	if (list == NULL)
+		return -1;
+
+	bf_data_t* nwdata = cgmalloc(sizeof(bf_data_t));
+	nwdata->data = cgmalloc(sizeof(bf_noncework_t));
+	nwdata->next = NULL;
+	nwdata->prev = NULL;
+
+	cg_memcpy(&NONCEWORK(nwdata)->cwork, &cwork, sizeof(bf_works_t));
+	cg_memcpy(&NONCEWORK(nwdata)->owork, &owork, sizeof(bf_works_t));
+	cg_memcpy(&NONCEWORK(nwdata)->chip_address, &chip_address, sizeof(bf_chip_address_t));
+	cg_memcpy(&NONCEWORK(nwdata)->src_address,  &src_address,  sizeof(bf_chip_address_t));
+	NONCEWORK(nwdata)->nonce = nonce;
+
+	LIST_PUSH_TAIL(list, nwdata);
+	list->count++;
+
+	return 0;
+}
+
+int8_t noncework_list_pop(bf_list_t* list)
+{
+	if (list == NULL)
+		return -1;
+
+	bf_data_t* nwdata = list->head;
+	if (nwdata != NULL) {
+		LIST_POP_HEAD(list);	
+		list->count--;
+		free(nwdata->data);
+		free(nwdata);
+	} else
+		return -1;
+
+	return 0;
+}
+
+/* renonce list primitives */
+bf_list_t* renonce_list_init(void)
+{
+	bf_list_t* list = cgmalloc(sizeof(bf_list_t));
+	list->head = NULL;
+	list->tail = NULL;
+	list->count = 0;
+	pthread_mutex_init(&list->lock, NULL);
+
+	return list;
+}
+
+int8_t renonce_list_deinit(bf_list_t* list)
+{
+	if (list == NULL)
+		return -1;
+
+	/* free renonce list */
+	L_LOCK(list);
+	while (list->head != NULL) {
+		bf_data_t* rdata = list->head;
+		LIST_POP_HEAD(list);
+
+		free(rdata->data);
+		free(rdata);
+	}
+	L_UNLOCK(list);
+
+	pthread_mutex_destroy(&list->lock);
+	list->count = 0;
+	free(list);
+
+	return 0;
+}
+
+int8_t renonce_list_push(bf_list_t* list, uint32_t id, uint32_t nonce, bf_chip_address_t src_address,
+		bf_works_t cwork, bf_works_t owork)
+{
+	if (list == NULL)
+		return -1;
+
+	bf_data_t* rdata = cgmalloc(sizeof(bf_data_t));
+	rdata->data = cgmalloc(sizeof(bf_renonce_t));
+	rdata->next = NULL;
+	rdata->prev = NULL;
+
+	cg_memcpy(&RENONCE(rdata)->cwork, &cwork, sizeof(bf_works_t));
+	cg_memcpy(&RENONCE(rdata)->owork, &owork, sizeof(bf_works_t));
+	cg_memcpy(&RENONCE(rdata)->src_address, &src_address, sizeof(bf_chip_address_t));
+	RENONCE(rdata)->id    = id;
+	RENONCE(rdata)->nonce = nonce;
+	RENONCE(rdata)->stage = RENONCE_STAGE0;
+	RENONCE(rdata)->sent      = false;
+	RENONCE(rdata)->received  = false;
+	RENONCE(rdata)->match     = false;
+
+	LIST_PUSH_TAIL(list, rdata);
+	list->count++;
+
+	return 0;
+}
+
+int8_t renonce_list_pop(bf_list_t* list)
+{
+	if (list == NULL)
+		return -1;
+
+	bf_data_t* rdata = list->head;
+	if (rdata != NULL) {
+		LIST_POP_HEAD(list);	
+		list->count--;
+		free(rdata->data);
+		free(rdata);
+	} else
+		return -1;
+
+	return 0;
+}
+
+int8_t renonce_list_remove(bf_list_t* list, bf_data_t* rdata)
+{
+	if (list == NULL)
+		return -1;
+
+	if (rdata != NULL) {
+		LIST_REMOVE(list, rdata);
+		list->count--;
+		free(rdata->data);
+		free(rdata);
+	} else
+		return -1;
+
+	return 0;
+}

+ 383 - 0
bf16-bitfury16.h

@@ -0,0 +1,383 @@
+#ifndef BF16_BITFURY16_H
+#define BF16_BITFURY16_H
+
+#include <stdint.h>
+
+#include "bf16-spidevice.h"
+
+/******************************************************
+ *                      Macros
+ ******************************************************/
+
+#define LIST_PUSH_HEAD(_list, _item) { \
+	if (_list->tail == NULL) {         \
+		_list->tail = _item;           \
+		_list->head = _item;           \
+	} else {                           \
+		_list->head->prev = _item;     \
+		_item->next = _list->head;     \
+		_list->head = _item;           \
+	}                                  \
+}
+
+#define LIST_PUSH_TAIL(_list, _item) { \
+	if (_list->head == NULL) {         \
+		_list->head = _item;           \
+		_list->tail = _item;           \
+	} else {                           \
+		_list->tail->next = _item;     \
+		_item->prev = _list->tail;     \
+		_list->tail = _item;           \
+	}                                  \
+}
+
+#define LIST_POP_HEAD(_list) {           \
+	if (_list->head != _list->tail) {    \
+		_list->head = _list->head->next; \
+		_list->head->prev = NULL;        \
+	} else {                             \
+		_list->head = NULL;              \
+		_list->tail = NULL;              \
+	}                                    \
+}
+
+#define LIST_POP_TAIL(_list) {           \
+	if (_list->head != _list->tail) {    \
+		_list->tail = _list->tail->prev; \
+		_list->tail->next = NULL;        \
+	} else {                             \
+		_list->head = NULL;              \
+		_list->tail = NULL;              \
+	}                                    \
+}
+
+#define LIST_REMOVE(_list, _item) {          \
+	if (_list->head != _list->tail) {        \
+		if (_list->head == _item) {          \
+			_list->head = _list->head->next; \
+			_list->head->prev = NULL;        \
+		} else if (_list->tail == _item) {   \
+			_list->tail = _list->tail->prev; \
+			_list->tail->next = NULL;        \
+		} else {                             \
+			bf_data_t* prev;                 \
+			bf_data_t* next;                 \
+			prev = _item->prev;              \
+			next = _item->next;              \
+			prev->next = next;               \
+			next->prev = prev;               \
+		}	                                 \
+	} else {                                 \
+		_list->head = NULL;                  \
+		_list->tail = NULL;                  \
+	}                                        \
+}
+
+#define L_LOCK(_list)   pthread_mutex_lock  (&_list->lock);
+#define L_UNLOCK(_list) pthread_mutex_unlock(&_list->lock);
+
+/******************************************************
+ *                    Constants
+ ******************************************************/
+
+#define CHIP_COEFF	        4.295
+#define CHIP_CMD_NUM        8
+#define CMD_BUFFER_LEN      4096
+
+/******************************************************
+ *                   Enumerations
+ ******************************************************/
+
+typedef enum {
+	CHIP_CMD_TASK_STATUS,
+	CHIP_CMD_TASK_WRITE,
+	CHIP_CMD_TASK_SWITCH,
+	CHIP_CMD_READ_NONCE  = 0x04,
+	CHIP_CMD_SET_CLOCK   = 0x08,
+	CHIP_CMD_TOGGLE      = 0x10,
+	CHIP_CMD_SET_MASK    = 0x20,
+	CHIP_CMD_CREATE_CHANNEL
+} bf_cmd_code_t;
+
+/* chip state enumeration */
+typedef enum {
+	UNINITIALIZED,
+	TOGGLE_SET,
+	CLOCK_SET,
+	MASK_SET,
+	TASK_SENT,
+	TASK_SWITCHED,
+	FAILING,
+	DISABLED
+} bf_chip_status_t;
+
+enum bf_channel_id {
+	BF250_NONE  = 0x00,
+	BF250_LOCAL = 0x04,
+	BF250_CHAN1 = 0x06,
+	BF250_CHAN2 = 0x07
+};
+
+typedef enum {
+	RENONCE_STAGE0,
+	RENONCE_STAGE1,
+	RENONCE_STAGE2,
+	RENONCE_STAGE3,
+	RENONCE_STAGE_FINISHED
+} bf_renonce_stage_t;
+
+/******************************************************
+ *                 Type Definitions
+ ******************************************************/
+
+/* list definition */
+struct bf_data {
+	struct bf_data* next;
+	struct bf_data* prev;
+	void*           data;
+};
+
+typedef struct bf_data bf_data_t;
+
+typedef struct {
+	bf_data_t*      head;
+	bf_data_t*      tail;
+	uint32_t        count;
+	pthread_mutex_t lock;
+} bf_list_t;
+
+/* general chip command staff */
+typedef struct {
+	bf_cmd_code_t  cmd_code;
+	char           cmd_description[32];
+} bf_cmd_description_t;
+
+typedef struct {
+	int8_t         board_id;
+	int8_t         bcm250_id;
+	int8_t         chip_id;
+} bf_chip_address_t;
+
+typedef struct {
+	bf_chip_address_t   chip_address;
+	uint8_t             depth;
+	bf_cmd_code_t       cmd_code;
+	uint8_t             data_length;
+	uint8_t             tx[128];
+	uint8_t             rx[64];
+	uint8_t             status;
+	uint8_t             checksum;
+	bool                checksum_error;
+	uint8_t             nonce_checksum;
+	bool                nonce_checksum_error;
+} bf_command_t;
+
+/* work stuff */
+typedef struct {
+	uint32_t    midstate[8];
+	uint32_t    m7;
+	uint32_t    ntime;
+	uint32_t    nbits;
+} bf_payload_t;
+
+typedef struct {
+	struct work*    work;
+	bf_payload_t    payload;
+	bool            rolled;
+	time_t          generated;
+} bf_workd_t;
+
+typedef struct {
+	struct work     work;
+	bf_payload_t    payload;
+	uint8_t         task[80];
+} bf_works_t;
+
+/* nonceworker stuff */
+typedef struct {
+	bf_chip_address_t   chip_address;
+	bf_chip_address_t   src_address;
+	bf_works_t          cwork;
+	bf_works_t          owork;
+	uint32_t            nonce;
+} bf_noncework_t;
+
+/* renonceworker stuff */
+typedef struct {
+	bf_chip_address_t   src_address;
+	uint32_t            nonce;
+} bf_renoncework_t;
+
+/* nonce recalculation staff */
+/* nonce list */
+typedef struct {
+	uint32_t            nonce;
+} bf_nonce_t;
+
+/* task + nonces list */
+typedef struct {
+	uint32_t            id;
+	uint32_t            nonce;
+	bf_works_t          cwork;
+	bf_works_t          owork;
+	bf_chip_address_t   src_address;
+	bf_renonce_stage_t  stage;
+	bool                sent;
+	bool                received;
+	bool                match;
+} bf_renonce_t;
+
+/* command buffer staff */
+typedef struct bf_cmd {
+	bf_chip_address_t   chip_address;   /* address of chip calculating result */
+	bf_chip_address_t   src_address;    /* track chip address during nonce recalculation */
+	bf_works_t          work;
+	uint32_t            id;             /* renonce id */
+	uint8_t             depth;
+	bf_cmd_code_t       cmd_code;
+	uint8_t             data_length;
+	uint8_t             checksum;
+} bf_cmd_t;
+
+#define CMD(_item)          ((bf_cmd_t *)          (_item->data))
+#define NONCE(_item)        ((bf_nonce_t *)        (_item->data))
+#define RENONCE(_item)      ((bf_renonce_t *)      (_item->data))
+#define NONCEWORK(_item)    ((bf_noncework_t *)    (_item->data))
+#define RENONCEWORK(_item)  ((bf_renoncework_t *)  (_item->data))
+#define WORKD(_item)        ((bf_workd_t *)        (_item->data))
+#define WORKS(_item)        ((bf_works_t *)        (_item->data))
+
+typedef struct {
+	bf_chip_address_t   chip_address;
+	bf_chip_address_t   src_address;
+	bf_works_t          work;
+	uint32_t            id;
+	bf_cmd_code_t       cmd_code;
+	uint8_t             status;
+	uint8_t             checksum_expected;
+	uint8_t             checksum_received;
+	bool                checksum_error;
+	uint8_t             nonce_checksum_expected;
+	uint8_t             nonce_checksum_received;
+	bool                nonce_checksum_error;
+} bf_cmd_status_t;
+
+typedef enum {
+	EMPTY,
+	TX_READY,
+	EXECUTED
+} bf_cmd_buffer_status_t;
+
+typedef struct {
+	bf_list_t*              cmd_list;
+
+	uint8_t*                tx_buffer;
+	uint8_t*                rx_buffer;
+	uint32_t                free_bytes; /* TX buffer bytes free */
+	uint32_t                tx_offset;
+	uint32_t                rx_offset;
+	bf_cmd_buffer_status_t  status;
+} bf_cmd_buffer_t;
+
+/******************************************************
+ *                    Structures
+ ******************************************************/
+
+/******************************************************
+ *               Static Function Declarations
+ ******************************************************/
+
+/******************************************************
+ *               Variables Definitions
+ ******************************************************/
+
+extern bf_cmd_description_t cmd_description[CHIP_CMD_NUM];
+
+/******************************************************
+ *               Function Definitions
+ ******************************************************/
+
+/* BF16 command primitives */
+uint8_t spi_command_init(bf_command_t* command, const uint8_t depth,
+		const bf_chip_address_t chip_address, const bf_cmd_code_t cmd_code,
+		const uint8_t data_length, const uint8_t* tx);
+uint8_t spi_command_exec(spi_channel_id_t spi_channel, bf_command_t* command, uint32_t* nonces);
+
+/* data preparation routines */
+uint8_t gen_clock_data(uint8_t clock, uint8_t prescaler, uint8_t data[4]);
+uint8_t gen_task_data(uint32_t* midstate, uint32_t merkle, uint32_t ntime,
+		uint32_t nbits, uint32_t mask, uint8_t* task);
+uint32_t gen_mask(uint32_t nonce, uint8_t nbits);
+
+/* SPI BCM250 primitives */
+uint8_t create_channel(spi_channel_id_t spi_channel, uint8_t* channel_path, uint8_t channel_length);
+uint8_t destroy_channel(spi_channel_id_t spi_channel, uint8_t depth);
+
+/* SPI BTC16 primitives  */
+void    spi_emit_reset(spi_channel_id_t spi_channel);
+uint8_t send_toggle(spi_channel_id_t spi_channel, uint8_t depth,
+		bf_chip_address_t chip_address);
+uint8_t set_clock(spi_channel_id_t spi_channel, uint8_t depth,
+		bf_chip_address_t chip_address, uint8_t clock);
+
+/* cmd buffer primitives */
+int8_t  cmd_buffer_init(bf_cmd_buffer_t* cmd_buffer);
+int8_t  cmd_buffer_deinit(bf_cmd_buffer_t* cmd_buffer);
+int8_t  cmd_buffer_clear(bf_cmd_buffer_t* cmd_buffer);
+int8_t  cmd_buffer_push(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth,
+		const bf_chip_address_t chip_address, const bf_chip_address_t src_address,
+		const bf_works_t work, const uint32_t id,
+		const bf_cmd_code_t cmd_code, const uint8_t data_length, const uint8_t* tx);
+int8_t  cmd_buffer_pop(bf_cmd_buffer_t* cmd_buffer, bf_cmd_status_t* cmd_status, uint32_t* nonces);
+int8_t  cmd_buffer_exec(spi_channel_id_t spi_channel, bf_cmd_buffer_t* cmd_buffer);
+
+int8_t  cmd_buffer_push_create_channel(bf_cmd_buffer_t* cmd_buffer, uint8_t* channel_path,
+		uint8_t channel_length);
+int8_t  cmd_buffer_push_destroy_channel(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth);
+
+int8_t  cmd_buffer_push_send_toggle(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth,
+		const bf_chip_address_t chip_address);
+int8_t  cmd_buffer_push_set_clock(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth,
+		const bf_chip_address_t chip_address, uint8_t clock);
+int8_t  cmd_buffer_push_set_mask(bf_cmd_buffer_t* cmd_buffer, const uint8_t depth,
+		const bf_chip_address_t chip_address, uint8_t mask);
+
+char*   get_cmd_description(bf_cmd_code_t cmd_code);
+
+/* dynamic work list primitives */
+bf_list_t* workd_list_init(void);
+int8_t workd_list_deinit(bf_list_t* list, struct cgpu_info *bitfury);
+int8_t workd_list_push(bf_list_t* list, bf_workd_t* work);
+int8_t workd_list_pop(bf_list_t* list, struct cgpu_info *bitfury);
+int8_t workd_list_remove(bf_list_t* list, bf_works_t* work);
+
+/* nonce list primitives */
+bf_list_t* nonce_list_init(void);
+int8_t nonce_list_deinit(bf_list_t* list);
+int8_t nonce_list_push(bf_list_t* list, uint32_t nonce);
+uint32_t nonce_list_pop(bf_list_t* list);
+
+/* noncework list primitives */
+bf_list_t* noncework_list_init(void);
+int8_t noncework_list_deinit(bf_list_t* list);
+int8_t noncework_list_push(bf_list_t* list, bf_chip_address_t chip_address,
+		bf_chip_address_t src_address, bf_works_t cwork, bf_works_t owork, uint32_t nonce);
+int8_t noncework_list_pop(bf_list_t* list);
+
+bf_list_t* renoncework_list_init(void);
+int8_t renoncework_list_deinit(bf_list_t* list);
+int8_t renoncework_list_push(bf_list_t* list, bf_chip_address_t src_address, uint32_t nonce);
+int8_t renoncework_list_pop(bf_list_t* list);
+	
+/* renonce list primitives */
+bf_list_t* renonce_list_init(void);
+int8_t renonce_list_deinit(bf_list_t* list);
+int8_t renonce_list_push(bf_list_t* list, uint32_t id, uint32_t nonce, bf_chip_address_t src_address,
+		bf_works_t cwork, bf_works_t owork);
+int8_t renonce_list_pop(bf_list_t* list);
+int8_t renonce_list_remove(bf_list_t* list, bf_data_t* rdata);
+
+uint8_t find_nonces(uint32_t* curr_nonces, uint32_t* prev_nonces, uint32_t* valid_nonces);
+bool match_nonce(uint32_t nonce, uint32_t mask, uint8_t nbits);
+
+#endif /* BF16_BITFURY16_H */

+ 122 - 0
bf16-brd-control.c

@@ -0,0 +1,122 @@
+#include "bf16-brd-control.h"
+#include "miner.h"
+
+#define BV(x)  (1 << x)
+
+static uint32_t ctrl_read(uint8_t gpio, uint8_t reg)
+{
+	gpio_rq_t rq;
+
+	rq.gpioIndex = gpio;
+	rq.regIndex = reg;
+	gpio_read_ctrl(&rq);
+
+	return rq.data;
+}
+
+static int8_t ctrl_write(uint8_t gpio, uint8_t reg, uint32_t data)
+{
+	gpio_rq_t rq;
+
+	rq.gpioIndex = gpio;
+	rq.regIndex = reg;
+	rq.data = data;
+	return gpio_write_ctrl(&rq);
+}
+
+void brd_init(void)
+{
+	uint32_t data = ctrl_read(GPIO0_INDEX, OE_REG_INDEX);
+	data &= ~(BV(CH1_MSP_RST_PIN) | BV(LED_GREEN_PIN));
+	ctrl_write(GPIO0_INDEX, OE_REG_INDEX, data);
+
+	data = ctrl_read(GPIO1_INDEX, OE_REG_INDEX);
+	data &= ~(BV(LED_RED_PIN) | BV(CH2_MSP_RST_PIN) | BV(CH1_SPI_RES_PIN));
+	ctrl_write(GPIO1_INDEX, OE_REG_INDEX, data);
+
+	data = ctrl_read(GPIO2_INDEX, OE_REG_INDEX);
+	data &= ~(BV(BUZZER_PIN) | BV(CH2_SPI_RES_PIN));
+	ctrl_write(GPIO2_INDEX, OE_REG_INDEX, data);
+}
+
+int get_hw_ver(void)
+{
+	uint32_t data1 = ctrl_read(GPIO2_INDEX, DATAIN_REG_INDEX);
+	uint32_t data2 = ctrl_read(GPIO1_INDEX, DATAIN_REG_INDEX);
+
+	uint8_t result = BIT_STATE(data1, BRD_VER0_PIN);
+	result |= BIT_STATE(data1, BRD_VER1_PIN) ? 2 : 0;
+	result |= BIT_STATE(data1, BRD_VER2_PIN) ? 4 : 0;
+	result |= BIT_STATE(data2, BRD_VER3_PIN) ? 8 : 0;
+
+	return result;
+}
+
+int get_btn_fr(void)
+{
+	uint32_t data = ctrl_read(GPIO2_INDEX, DATAIN_REG_INDEX);
+	return BIT_INV_STATE(data, BRD_BUT1_PIN);
+}
+
+int get_btn_discovery(void)
+{
+	uint32_t data = ctrl_read(GPIO0_INDEX, DATAIN_REG_INDEX);
+	return BIT_INV_STATE(data, BRD_BUT2_PIN);
+}
+
+int get_ch1_det(void)
+{
+	uint32_t data = ctrl_read(GPIO0_INDEX, DATAIN_REG_INDEX);
+	return BIT_INV_STATE(data, BRD_DET1_PIN);
+}
+
+int get_ch2_det(void)
+{
+	uint32_t data = ctrl_read(GPIO1_INDEX, DATAIN_REG_INDEX);
+	return BIT_INV_STATE(data, BRD_DET2_PIN);
+}
+
+static uint8_t _direct_state_reg_index(uint8_t state)
+{
+	return (state != 0) ? DATASET_REG_INDEX : DATACLR_REG_INDEX;
+}
+
+static uint8_t _inverse_state_reg_index(uint8_t state)
+{
+	return (state != 0) ? DATACLR_REG_INDEX : DATASET_REG_INDEX;
+}
+
+int8_t set_buzzer(uint8_t state)
+{
+	return ctrl_write(GPIO2_INDEX, _direct_state_reg_index(state), BV(BUZZER_PIN));
+}
+
+int8_t set_led_green(uint8_t state)
+{
+	return ctrl_write(GPIO0_INDEX, _direct_state_reg_index(state), BV(LED_GREEN_PIN));
+}
+
+int8_t set_led_red(uint8_t state)
+{
+	return ctrl_write(GPIO1_INDEX, _direct_state_reg_index(state), BV(LED_RED_PIN));
+}
+
+int8_t set_ch1_rst(uint8_t state)
+{
+	return ctrl_write(GPIO0_INDEX, _inverse_state_reg_index(state), BV(CH1_MSP_RST_PIN));
+}
+
+int8_t set_ch2_rst(uint8_t state)
+{
+	return ctrl_write(GPIO1_INDEX, _inverse_state_reg_index(state), BV(CH2_MSP_RST_PIN));
+}
+
+int8_t set_ch1_spi(uint8_t state)
+{
+	return ctrl_write(GPIO2_INDEX, _direct_state_reg_index(state), BV(CH1_SPI_RES_PIN));
+}
+
+int8_t set_ch2_spi(uint8_t state)
+{
+	return ctrl_write(GPIO1_INDEX, _direct_state_reg_index(state), BV(CH2_SPI_RES_PIN));
+}

+ 43 - 0
bf16-brd-control.h

@@ -0,0 +1,43 @@
+#ifndef BF16_BRD_CONTROL_H
+#define BF16_BRD_CONTROL_H
+
+#include <stdint.h>
+
+#include "bf16-gpiodevice.h"
+
+#define BIT_STATE(data, pin) ( (data & BV(pin)) >> pin )
+#define BIT_INV_STATE(data, pin) ( BIT_STATE(data, pin) ^ 1 )
+
+#define BUZZER_PIN      2   //  GPIO2[2],  GPIO_66, X8.7
+#define BRD_VER0_PIN    3   //  GPIO2[3],  GPIO_67, X8.8
+#define BRD_VER1_PIN    5   //  GPIO2[5],  GPIO_69, X8.9
+#define BRD_VER2_PIN    4   //  GPIO2[4],  GPIO_68, X8.10
+#define BRD_VER3_PIN    13  //  GPIO1[13], GPIO_45, X8.11
+#define BRD_DET2_PIN    12  //  GPIO1[12], GPIO_44, X8.12
+#define BRD_DET1_PIN    23  //  GPIO0[23], GPIO_23, X8.13
+#define LED_GREEN_PIN   26  //  GPIO0[26], GPIO_26, X8.14
+#define LED_RED_PIN     15  //  GPIO1[15], GPIO_47, X8.15
+#define CH2_MSP_RST_PIN 14  //  GPIO1[14], GPIO_46, X8.16
+#define CH1_MSP_RST_PIN 27  //  GPIO0[27], GPIO_27, X8.17
+#define BRD_BUT1_PIN    1   //  GPIO2[1],  GPIO_65, X8.18
+#define BRD_BUT2_PIN    22  //  GPIO0[22], GPIO_22, X8.19
+#define CH1_SPI_RES_PIN 22  //  GPIO2[22], GPIO_86, X8.27
+#define CH2_SPI_RES_PIN 29  //  GPIO1[29], GPIO_61, X8.26
+
+
+void brd_init(void);
+
+int get_hw_ver(void);
+int get_btn_fr(void);
+int get_btn_discovery(void);
+int get_ch1_det(void);
+int get_ch2_det(void);
+int8_t set_buzzer(uint8_t state);
+int8_t set_led_green(uint8_t state);
+int8_t set_led_red(uint8_t state);
+int8_t set_ch1_rst(uint8_t state);
+int8_t set_ch2_rst(uint8_t state);
+int8_t set_ch1_spi(uint8_t state);
+int8_t set_ch2_spi(uint8_t state);
+
+#endif /* BF16_BRD_CONTROL_H */

+ 317 - 0
bf16-communication.c

@@ -0,0 +1,317 @@
+#include "bf16-communication.h"
+#include "bf16-ctrldevice.h"
+#include "bf16-gpiodevice.h"
+#include "miner.h"
+
+#define MSP_BUFF_SIZE   96
+
+static device_t*   spi0_device;
+static device_t*   spi1_device;
+
+static device_t*   uart1_device;
+static device_t*   uart2_device;
+
+static device_t*   ctrl_device;
+static device_t*   gpio_device;
+
+/* transfer functions */
+int8_t device_spi_transfer(spi_channel_id_t channel_id, uint8_t* data, int size)
+{
+	switch (channel_id) {
+	case SPI_CHANNEL1:
+		memset(spi0_device->tx, 0, spi0_device->size);
+		memset(spi0_device->rx, 0, spi0_device->size);
+		spi0_device->datalen = size;
+
+		cg_memcpy(spi0_device->tx, data, size);
+
+		spi_transfer(spi0_device);
+
+		cg_memcpy(data, spi0_device->rx, size);
+		break;
+	case SPI_CHANNEL2:
+		memset(spi1_device->tx, 0, spi1_device->size);
+		memset(spi1_device->rx, 0, spi1_device->size);
+		spi1_device->datalen = size;
+
+		cg_memcpy(spi1_device->tx, data, size);
+
+		spi_transfer(spi1_device);
+
+		cg_memcpy(data, spi1_device->rx, size);
+		break;
+	}
+
+	return 0;
+}
+
+int8_t device_spi_txrx(spi_channel_id_t channel_id, uint8_t* tx, uint8_t* rx, int size)
+{
+	switch (channel_id) {
+	case SPI_CHANNEL1:
+		memset(spi0_device->tx, 0, spi0_device->size);
+		memset(spi0_device->rx, 0, spi0_device->size);
+		spi0_device->datalen = size;
+
+		cg_memcpy(spi0_device->tx, tx, size);
+
+		spi_transfer(spi0_device);
+
+		cg_memcpy(rx, spi0_device->rx, size);
+		break;
+	case SPI_CHANNEL2:
+		memset(spi1_device->tx, 0, spi1_device->size);
+		memset(spi1_device->rx, 0, spi1_device->size);
+		spi1_device->datalen = size;
+
+		cg_memcpy(spi1_device->tx, tx, size);
+
+		spi_transfer(spi1_device);
+
+		cg_memcpy(rx, spi1_device->rx, size);
+		break;
+	}
+
+	return 0;
+}
+
+static void add_crc(char* data)
+{
+	uint8_t crc = 0;
+
+	while (*data) {
+		crc += *data;
+		data++;
+	}
+
+	sprintf(data, "#%d\n", crc);
+}
+
+int8_t device_uart_transfer(uart_channel_id_t channel_id, char* cmd)
+{
+	uint8_t buff[MSP_BUFF_SIZE];
+	memset(buff, 0, MSP_BUFF_SIZE);
+
+	uint16_t cmdlen = strlen(cmd);
+	if (cmdlen > 0) {
+		(cmdlen == 1) ? sprintf((char *)buff, "%s:", cmd) : sprintf((char *)buff, "%s", cmd);
+		add_crc((char *)buff);
+	} 
+
+	switch (channel_id) {
+	case UART_CHANNEL1:
+		memset(uart1_device->tx, 0, uart1_device->size);
+		memset(uart1_device->rx, 0, uart1_device->size);
+		uart1_device->datalen = strlen((char *)buff);
+
+		cg_memcpy(uart1_device->tx, buff, uart1_device->datalen);
+
+		return uart_transfer(uart1_device);
+		break;
+	case UART_CHANNEL2:
+		memset(uart2_device->tx, 0, uart2_device->size);
+		memset(uart2_device->rx, 0, uart2_device->size);
+		uart2_device->datalen = strlen((char *)buff);
+
+		cg_memcpy(uart2_device->tx, buff, uart2_device->datalen);
+
+		return uart_transfer(uart2_device);
+		break;
+	}
+
+	return 0;
+}
+
+int16_t device_uart_txrx(uart_channel_id_t channel_id, char* cmd, char* data)
+{
+	uint8_t buff[MSP_BUFF_SIZE];
+	memset(buff, 0, MSP_BUFF_SIZE);
+
+	uint16_t cmdlen = strlen(cmd);
+	if (cmdlen > 0) {
+		(cmdlen == 1) ? sprintf((char *)buff, "%s:", cmd) : sprintf((char *)buff, "%s", cmd);
+		add_crc((char *)buff);
+	} 
+
+	switch (channel_id) {
+	case UART_CHANNEL1:
+		memset(uart1_device->tx, 0, uart1_device->size);
+		memset(uart1_device->rx, 0, uart1_device->size);
+		uart1_device->datalen = strlen((char *)buff);
+
+		cg_memcpy(uart1_device->tx, buff, uart1_device->datalen);
+
+		if (uart_transfer(uart1_device) < 0)
+			return -1;
+
+		cg_memcpy(data, uart1_device->rx, uart1_device->datalen);
+
+		return uart1_device->datalen;
+
+		break;
+	case UART_CHANNEL2:
+		memset(uart2_device->tx, 0, uart2_device->size);
+		memset(uart2_device->rx, 0, uart2_device->size);
+		uart2_device->datalen = strlen((char *)buff);
+
+		cg_memcpy(uart2_device->tx, buff, uart2_device->datalen);
+
+		if (uart_transfer(uart2_device) < 0)
+			return -1;
+
+		cg_memcpy(data, uart2_device->rx, uart2_device->datalen);
+
+		return uart2_device->datalen;
+
+		break;
+	}
+
+	return 0;
+}
+
+int8_t device_ctrl_transfer(uint8_t channel_id, int state, int fn)
+{
+	int8_t ret = 0;
+	char* cmd = get_ctrl_data(channel_id, state, fn);
+
+	memset(ctrl_device->tx, 0, ctrl_device->size);
+	memset(ctrl_device->rx, 0, ctrl_device->size);
+	ctrl_device->datalen = strlen(cmd) + 1;
+
+	cg_memcpy(ctrl_device->tx, cmd, ctrl_device->datalen);
+
+	ret = ctrl_transfer(ctrl_device);
+
+	free(cmd);
+
+	return ret;
+}
+
+int8_t device_ctrl_txrx(uint8_t channel_id, int state, int fn, char* data)
+{
+	int8_t ret = 0;
+	char* cmd = get_ctrl_data(channel_id, state, fn);
+
+	memset(ctrl_device->tx, 0, ctrl_device->size);
+	memset(ctrl_device->rx, 0, ctrl_device->size);
+	ctrl_device->datalen = strlen(cmd) + 1;
+
+	cg_memcpy(ctrl_device->tx, cmd, ctrl_device->datalen);
+
+	ret = ctrl_transfer(ctrl_device);
+
+	cg_memcpy(data, ctrl_device->rx, ctrl_device->datalen);
+
+	free(cmd);
+
+	return ret;
+}
+
+/* open device functions */
+int8_t open_spi_device(spi_channel_id_t channel_id)
+{
+	switch (channel_id) {
+	case SPI_CHANNEL1:
+		if ((spi0_device = malloc(sizeof(device_t))) == NULL)
+			quit(1, "Failed to allocate spi_device1 memory: %s", strerror(errno));
+
+		memset(spi0_device, 0, sizeof(device_t));
+		return spi_init(spi0_device, channel_id, 1, SPI_SPEED, SPI_BUFFER_SIZE);
+		break;
+	case SPI_CHANNEL2:
+		if ((spi1_device = malloc(sizeof(device_t))) == NULL)
+			quit(1, "Failed to allocate spi_device2 memory: %s", strerror(errno));
+
+		memset(spi1_device, 0, sizeof(device_t));
+		return spi_init(spi1_device, channel_id, 1, SPI_SPEED, SPI_BUFFER_SIZE);
+		break;
+	}
+
+	return 0;
+}
+
+int8_t open_uart_device(uart_channel_id_t channel_id)
+{
+	switch (channel_id) {
+	case UART_CHANNEL1:
+		if ((uart1_device = malloc(sizeof(device_t))) == NULL)
+			quit(1, "Failed to allocate uart_device1 memory: %s", strerror(errno));
+
+		memset(uart1_device, 0, sizeof(device_t));
+		return uart_init(uart1_device, channel_id, 0, B115200, UART_BUFFER_SIZE);
+		break;
+
+	case UART_CHANNEL2:
+		if ((uart2_device = malloc(sizeof(device_t))) == NULL)
+			quit(1, "Failed to allocate uart_device2 memory: %s", strerror(errno));
+
+		memset(uart2_device, 0, sizeof(device_t));
+		return uart_init(uart2_device, channel_id, 0, B115200, UART_BUFFER_SIZE);
+		break;
+	}
+
+	return 0;
+}
+
+int8_t open_ctrl_device(void)
+{
+	if ((ctrl_device = malloc(sizeof(device_t))) == NULL)
+		quit(1, "Failed to allocate ctrl_device memory: %s", strerror(errno));
+
+	memset(ctrl_device, 0, sizeof(device_t));
+
+	if ((gpio_device = malloc(sizeof(device_t))) == NULL)
+		quit(1, "Failed to allocate gpio_device memory: %s", strerror(errno));
+
+	memset(gpio_device, 0, sizeof(device_t));
+
+	if (gpio_init(gpio_device, gpio_device_name, GPIO_BUFFER_SIZE) < 0)
+		quit(1, "Failed to open [%s] device in open_ctrl_device", gpio_device_name);
+
+	applog(LOG_INFO, "BF16: opened [%s] device", gpio_device_name);
+
+	return ctrl_init(ctrl_device, ctrl_device_name, CTRL_BUFFER_SIZE);
+}
+
+/* close device functions */
+int8_t close_spi_device(spi_channel_id_t channel_id)
+{
+	switch (channel_id) {
+	case SPI_CHANNEL1:
+		spi_release(spi0_device);
+		free(spi0_device);
+		break;
+	case SPI_CHANNEL2:
+		spi_release(spi1_device);
+		free(spi1_device);
+		break;
+	}
+
+	return 0;
+}
+
+int8_t close_uart_device(uart_channel_id_t channel_id)
+{
+	switch (channel_id) {
+	case UART_CHANNEL1:
+		uart_release(uart1_device);
+		free(uart1_device);
+		break;
+	case UART_CHANNEL2:
+		uart_release(uart2_device);
+		free(uart2_device);
+		break;
+	}
+
+	return 0;
+}
+
+int8_t close_ctrl_device(void)
+{
+	ctrl_release(ctrl_device);
+	gpio_release(gpio_device);
+	free(ctrl_device);
+	free(gpio_device);
+
+	return 0;
+}

+ 23 - 0
bf16-communication.h

@@ -0,0 +1,23 @@
+#ifndef BF16_COMMUNICATION_H
+#define BF16_COMMUNICATION_H
+
+#include <stdint.h>
+#include "bf16-spidevice.h"
+#include "bf16-uartdevice.h"
+
+int8_t open_spi_device(spi_channel_id_t channel_id);
+int8_t open_uart_device(uart_channel_id_t channel_id);
+int8_t open_ctrl_device(void);
+
+int8_t close_spi_device(spi_channel_id_t channel_id);
+int8_t close_uart_device(uart_channel_id_t channel_id);
+int8_t close_ctrl_device(void);
+
+int8_t device_spi_transfer(spi_channel_id_t channel_id, uint8_t* data, int size);
+int8_t device_spi_txrx(spi_channel_id_t channel_id, uint8_t* tx, uint8_t* rx, int size);
+int8_t device_uart_transfer(uart_channel_id_t channel_id, char* cmd);
+int16_t device_uart_txrx(uart_channel_id_t channel_id, char* cmd, char* data);
+int8_t device_ctrl_transfer(uint8_t channel_id, int state, int fn);
+int8_t device_ctrl_txrx(uint8_t channel_id, int state, int fn, char* data);
+
+#endif /* BF16_COMMUNICATION_H */

+ 182 - 0
bf16-ctrldevice.c

@@ -0,0 +1,182 @@
+#include "bf16-ctrldevice.h"
+#include "miner.h"
+
+#define MAX_TOKENS_ALLOWED  10
+
+char *ctrl_device_name  = "ctrl";
+
+static char *d_state_on  = "ON";
+static char *d_state_off = "OFF";
+
+static char* D_STATE(int state)
+{
+	return (state == 0) ? d_state_off : d_state_on;
+}
+
+char* get_ctrl_data(int channel, int state, int fn)
+{
+	char *request = malloc(CTRL_BUFFER_SIZE);
+	memset(request, 0, CTRL_BUFFER_SIZE);
+	int index = (state == 0) ? 0 : 1;
+
+	switch (fn) {
+		case F_BUZZER:
+			sprintf(request, "%s=%s\n", BUZZER, D_STATE(index));
+			break;
+		case F_LED1:
+			sprintf(request, "%s=%s\n", LED_GREEN, D_STATE(index));
+			break;
+		case F_LED2:
+			sprintf(request, "%s=%s\n", LED_RED, D_STATE(index));
+			break;
+		case F_BRST:
+			(channel == 1) ?
+				sprintf(request, "%s=%s\n", CH1_MSP_RST, D_STATE(index)) :
+				sprintf(request, "%s=%s\n", CH2_MSP_RST, D_STATE(index));
+			break;
+		case F_RST:
+			(channel == 1) ?
+				sprintf(request, "%s=%s\n", CH1_SPI_RES, D_STATE(index)) :
+				sprintf(request, "%s=%s\n", CH2_SPI_RES, D_STATE(index));
+			break;
+		case F_BDET:
+			(channel == 1) ?
+				sprintf(request, "%s=%s\n", CH1_BRD_DET, D_STATE(get_ch1_det())) :
+				sprintf(request, "%s=%s\n", CH2_BRD_DET, D_STATE(get_ch2_det()));
+			break;
+		case F_STAT:
+		default:
+			request[0] = '\n';
+	}
+
+	return request;
+}
+
+static int D_VALUE(char *value)
+{
+	if (strcasecmp(value, d_state_on) == 0)
+		return 1;
+
+	if (strcasecmp(value, d_state_off) == 0)
+		return 0;
+
+	return -1;
+}
+
+static int8_t D_FUNCTION(uint8_t value, char *cmd)
+{
+	if (strcmp(cmd, BUZZER) == 0)
+		return set_buzzer(value);
+	else if (strcmp(cmd, LED_GREEN) == 0)
+		return set_led_green(value);
+	else if (strcmp(cmd, LED_RED) == 0)
+		return set_led_red(value);
+	else if (strcmp(cmd, CH1_MSP_RST) == 0)
+		return set_ch1_rst(value);
+	else if (strcmp(cmd, CH2_MSP_RST) == 0)
+		return set_ch2_rst(value);
+	else if (strcmp(cmd, CH1_SPI_RES) == 0)
+		return set_ch1_spi(value);
+	else if (strcmp(cmd, CH2_SPI_RES) == 0)
+		return set_ch2_spi(value);
+	else
+		applog(LOG_ERR, "CTRL DEVICE, D_FUNCTION: unknown command: [%s]", cmd);
+
+	return -1;
+}
+
+static int split_tokens(char* cmd, char token, char** tokens)
+{
+	int len=0;
+	char *src, *ptr;
+
+	src = cmd;
+	ptr = cmd;
+	while ((*src != '\n') && (*src != '\r') && (*src != '\0') &&
+	       (len < MAX_TOKENS_ALLOWED)) {
+		if (*src == token) {
+			*src = 0;
+			tokens[len++] = ptr;
+			ptr = src + 1;
+		}
+		src++;
+	}
+
+	*src = 0;
+	if (src != ptr)
+		tokens[len++] = ptr;
+
+	return len;
+}
+
+int8_t ctrl_init(device_t* attr, char *device, uint16_t size)
+{
+	attr->device = device;
+	attr->mode = 0;
+	attr->speed = 0;
+	attr->bits = 0;
+	attr->size = size;
+	attr->rx = malloc(size);
+	attr->tx = malloc(size);
+
+	brd_init();
+
+	return 0;
+}
+
+int8_t ctrl_transfer(device_t *attr)
+{
+	char* cmd = (char*) attr->tx;
+	char* tokens[MAX_TOKENS_ALLOWED];
+	int8_t ret = 0;
+
+	int len = split_tokens(cmd, ';', tokens);
+	cmd = (char*) attr->rx;
+
+	if (len == 0) {
+		len += snprintf(cmd + len, attr->size, "%s=%d;", HW_VER,        get_hw_ver());
+		len += snprintf(cmd + len, attr->size, "%s=%s;", BTN_FR,        D_STATE(get_btn_fr()));
+		len += snprintf(cmd + len, attr->size, "%s=%s;", BTN_DISCOVERY, D_STATE(get_btn_discovery()));
+		len += snprintf(cmd + len, attr->size, "%s=%s;", CH1_DET,       D_STATE(get_ch1_det()));
+		len += snprintf(cmd + len, attr->size, "%s=%s\n",CH2_DET,       D_STATE(get_ch2_det()));
+		attr->datalen = len;
+	} else {
+		char* values[2];
+		int i;
+		int datalen = 0;
+
+		for (i = 0; i < len; i++) {
+			char *tok = tokens[i];
+			int n = split_tokens(tok, '=', values);
+
+			if (n == 2) {
+				int state = D_VALUE(values[1]);
+
+				if (state != -1) {
+					applog(LOG_DEBUG, "BF16: CTRL: token applying: %s>%s", values[0], values[1]);
+
+					if (strcasecmp(values[0], CH1_BRD_DET) == 0)
+						datalen += snprintf(cmd + datalen, attr->size, "%s:%s\n", CH1_DET, D_STATE(get_ch1_det()));
+					else if (strcasecmp(values[0], CH2_BRD_DET) == 0)
+						datalen += snprintf(cmd + datalen, attr->size, "%s:%s\n", CH2_DET, D_STATE(get_ch2_det()));
+					else
+						ret = D_FUNCTION(state, values[0]);
+				}
+			}
+		}
+
+		if (datalen == 0) {
+			datalen = 1;
+			cmd[0] = '\n';
+		} else
+			attr->datalen = datalen;
+	}
+
+	return ret;
+}
+
+void ctrl_release(device_t *attr)
+{
+	free(attr->rx);
+	free(attr->tx);
+}

+ 43 - 0
bf16-ctrldevice.h

@@ -0,0 +1,43 @@
+#ifndef BF16_CTRLDEVICE_H
+#define BF16_CTRLDEVICE_H
+
+#include "bf16-brd-control.h"
+#include "bf16-device.h"
+
+#define CTRL_BUFFER_SIZE    96
+
+/* CTRL functions */
+#define F_BUZZER        0
+#define F_LED1          1
+#define F_LED2          2
+#define F_BRST          3
+#define F_RST           4
+#define F_BDET          5
+#define F_STAT          6
+
+/* read in signals */
+#define HW_VER          "HW_VER"
+#define BTN_FR          "BTN_FR"
+#define BTN_DISCOVERY   "BTN_DISCOVERY"
+#define CH1_DET         "CH1_DET"
+#define CH2_DET         "CH2_DET"
+
+/* write out signals */
+#define BUZZER          "BUZZER"
+#define LED_GREEN       "LED_GREEN"
+#define LED_RED         "LED_RED"
+#define CH1_MSP_RST     "CH1_MSP_RST"
+#define CH2_MSP_RST     "CH2_MSP_RST"
+#define CH1_SPI_RES     "CH1_SPI_RES"
+#define CH2_SPI_RES     "CH2_SPI_RES"
+#define CH1_BRD_DET     "CH1_BRD_DET"
+#define CH2_BRD_DET     "CH2_BRD_DET"
+
+extern char *ctrl_device_name;
+
+int8_t ctrl_init(device_t* attr, char *device, uint16_t size);
+int8_t ctrl_transfer(device_t *attr);
+void ctrl_release(device_t *attr);
+char* get_ctrl_data(int channel, int state, int fn);
+
+#endif /* BF16_CTRLDEVICE_H */

+ 21 - 0
bf16-device.h

@@ -0,0 +1,21 @@
+#ifndef BF16_DEVICE_H
+#define BF16_DEVICE_H
+
+#include <stdint.h>
+
+typedef struct {
+	char        *device;
+	int         fd;
+
+	uint8_t     mode;
+	uint8_t     bits;
+	uint32_t    speed;
+	uint16_t    delay;
+
+	uint16_t    datalen;
+	uint16_t    size;
+	uint8_t     *rx;
+	uint8_t     *tx;
+} device_t;
+
+#endif /* BF16_DEVICE_H */

+ 111 - 0
bf16-gpiodevice.c

@@ -0,0 +1,111 @@
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "bf16-gpiodevice.h"
+#include "driver-bitfury16.h"
+
+#include "miner.h"
+
+typedef struct {
+	unsigned int*   oe_addr;  // R/W, bit==1 -> input, bit==0 -> output,
+	unsigned int*   data_out; // R/W
+	unsigned int*   data_set; // W/O
+	unsigned int*   data_clr; // W/O
+	unsigned int*   data_in;  // R/O
+} gpio_attr_t;
+
+typedef struct {
+	unsigned int* data_control;
+} ctrl_attr_t;
+
+typedef struct {
+	gpio_attr_t gpio0;
+	gpio_attr_t gpio1;
+	gpio_attr_t gpio2;
+	gpio_attr_t gpio3;
+	ctrl_attr_t* ctrl;
+} mmap_attr_t;
+
+static mmap_attr_t mmap_base;
+char *gpio_device_name  = "/dev/mem";
+
+int8_t gpio_write_ctrl(gpio_rq_t* rq)
+{
+	if (rq->gpioIndex < MAX_GPIO_INDEX && rq->regIndex < MAX_REGISTER_INDEX) {
+		gpio_attr_t* gpio_addr = (void*) &mmap_base;
+		gpio_attr_t gpio = gpio_addr[rq->gpioIndex];
+
+		unsigned int** addr = (void*) &gpio;
+		*addr[rq->regIndex] = rq->data;
+
+		return 0;
+	}
+
+	return -1;
+}
+
+int gpio_read_ctrl(gpio_rq_t* rq)
+{
+	if (rq->gpioIndex < MAX_GPIO_INDEX && rq->regIndex < MAX_REGISTER_INDEX) {
+		gpio_attr_t* gpio_addr = (void*) &mmap_base;
+		gpio_attr_t gpio = gpio_addr[rq->gpioIndex];
+
+		unsigned int** addr = (void*) &gpio;
+		rq->data = *addr[rq->regIndex];
+		return 0;
+	}
+
+	return -1;
+}
+
+void* map_gpio(gpio_attr_t* gpioAttr, void* gpio)
+{
+	gpioAttr->oe_addr  = gpio + GPIO_OE;
+	gpioAttr->data_out = gpio + GPIO_DATAOUT;
+	gpioAttr->data_set = gpio + GPIO_SETDATAOUT;
+	gpioAttr->data_clr = gpio + GPIO_CLEARDATAOUT;
+	gpioAttr->data_in  = gpio + GPIO_DATAIN;
+
+	return gpio;
+}
+
+void* mmap_open(const char *device, uint32_t base, uint32_t size)
+{
+	int fd = open(device, O_RDWR | O_SYNC);
+	if (fd < 0)
+		quit(1, "Failed to open /dev/mem: %s", strerror(errno));
+
+	void* mmap_addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, base);
+	if (mmap_addr == MAP_FAILED)
+		quit(1, "Failed to mmap GPIO [%08x]: %s", base, strerror(errno));
+
+	close(fd);
+
+	return mmap_addr;
+}
+
+int8_t gpio_init(device_t* attr, char *device, uint16_t size)
+{
+	attr->device = device;
+	attr->mode = 0;
+	attr->speed = 0;
+	attr->bits = 0;
+	attr->size = size;
+	attr->rx = malloc(size);
+	attr->tx = malloc(size);
+
+	map_gpio(&mmap_base.gpio0, mmap_open(device, GPIO0_START_ADDR, GPIO_SIZE));
+	map_gpio(&mmap_base.gpio1, mmap_open(device, GPIO1_START_ADDR, GPIO_SIZE));
+	map_gpio(&mmap_base.gpio2, mmap_open(device, GPIO2_START_ADDR, GPIO_SIZE));
+	map_gpio(&mmap_base.gpio3, mmap_open(device, GPIO3_START_ADDR, GPIO_SIZE));
+
+	return 0;
+}
+
+
+void gpio_release(device_t *attr)
+{
+	free(attr->rx);
+	free(attr->tx);
+}

+ 66 - 0
bf16-gpiodevice.h

@@ -0,0 +1,66 @@
+#ifndef BF16_GPIODEVICE_H
+#define BF16_GPIODEVICE_H
+
+#include "bf16-device.h"
+
+#define GPIO_BUFFER_SIZE 64
+
+/* GPIO/CTRL function types */
+#define GPIO0_INDEX         0
+#define GPIO1_INDEX         1
+#define GPIO2_INDEX         2
+#define GPIO3_INDEX         3
+#define MAX_GPIO_INDEX      4
+
+#define GPIO_SIZE           0x2000
+#define GPIO0_START_ADDR    0x44E07000
+#define GPIO1_START_ADDR    0x4804C000
+#define GPIO2_START_ADDR    0x481AC000
+#define GPIO3_START_ADDR    0x481AE000
+
+#define GPIO_OE             0x134
+#define GPIO_SETDATAOUT     0x194
+#define GPIO_CLEARDATAOUT   0x190
+#define GPIO_DATAIN         0x138
+#define GPIO_DATAOUT        0x13C
+
+#define OE_REG_INDEX        0
+#define DATAOUT_REG_INDEX   1
+#define DATASET_REG_INDEX   2
+#define DATACLR_REG_INDEX   3
+#define DATAIN_REG_INDEX    4
+#define MAX_REGISTER_INDEX  5
+
+typedef struct {
+	uint32_t    gpioIndex;
+	uint32_t    regIndex;
+	uint32_t    data;
+} gpio_rq_t;
+
+typedef struct {
+	uint32_t    oe_reg0;
+	uint32_t    out_reg0;
+	uint32_t    in_reg0;
+
+	uint32_t    oe_reg1;
+	uint32_t    out_reg1;
+	uint32_t    in_reg1;
+
+	uint32_t    oe_reg2;
+	uint32_t    out_reg2;
+	uint32_t    in_reg2;
+
+	uint32_t    oe_reg3;
+	uint32_t    out_reg3;
+	uint32_t    in_reg3;
+} gpio_resp_t;
+
+extern char *gpio_device_name;
+
+int gpio_read_ctrl(gpio_rq_t* rq);
+int8_t gpio_write_ctrl(gpio_rq_t* rq);
+
+int8_t gpio_init(device_t* attr, char *device, uint16_t size);
+void gpio_release(device_t *attr);
+
+#endif /* BF16_GPIODEVICE_H */

+ 502 - 0
bf16-mspcontrol.c

@@ -0,0 +1,502 @@
+#include "bf16-mspcontrol.h"
+
+#include "bf16-communication.h"
+#include "bf16-ctrldevice.h"
+
+static bool check_crc(char* data)
+{
+	uint8_t crc = 0;
+	char crcdata[8];
+
+	char* mid = strchr(data, '#');
+	char* end = strchr(data, '\n');
+
+	if (mid == NULL)
+		return false;
+
+	if (end == NULL)
+		return false;
+
+	while (data != mid) {
+		crc += *data;
+		data++;
+	}
+
+	uint8_t value_len = end - mid;
+	memset(crcdata, 0, sizeof(crcdata));
+	cg_memcpy(crcdata, mid + 1, value_len - 1);
+
+	int expected_crc = atoi(crcdata);
+
+	if (expected_crc != crc)
+		return false;
+
+	return true;
+}
+
+static char* get_int_val(char* data, char* name, char delim, int* value)
+{
+	char rname[16];
+	char rvalue[32];
+
+	char* end = strchr(data, delim);
+	if (end == NULL)
+		return NULL;
+
+	memset(rname, 0, sizeof(rname));
+	memset(rvalue, 0, sizeof(rvalue));
+
+	char* mid = strchr(data, ':');
+	if (mid == NULL)
+		return NULL;
+
+	uint8_t name_len  = mid - data;
+	uint8_t value_len = end - mid;
+
+	cg_memcpy(rname, data, name_len);
+	cg_memcpy(rvalue, mid + 1, value_len - 1);
+
+	if (strcmp(rname, name) == 0)
+		*value = atoi(rvalue);
+	else {
+		*value = 0;
+		return data;
+	}
+
+	return end + 1;
+}
+
+static char* get_str_val(char* data, char* name, char delim, char* value)
+{
+	char rname[16];
+	char rvalue[32];
+
+	char* end = strchr(data, delim);
+	if (end == NULL)
+		return NULL;
+
+	memset(rname, 0, sizeof(rname));
+	memset(rvalue, 0, sizeof(rvalue));
+
+	char* mid = strchr(data, ':');
+	if (mid == NULL)
+		return NULL;
+
+	uint8_t name_len  = mid - data;
+	uint8_t value_len = end - mid;
+
+	cg_memcpy(rname, data, name_len);
+	cg_memcpy(rvalue, mid + 1, value_len - 1);
+
+	if (strcmp(rname, name) == 0)
+		cg_memcpy(value, rvalue, strlen(rvalue));
+	else {
+		memset(value, '0', 16);
+		return data;
+	}
+
+	return end + 1;
+}
+
+void parse_board_detect(struct cgpu_info *bitfury, uint8_t board_id, char* data)
+{
+	struct bitfury16_info *info = (struct bitfury16_info *)(bitfury->device_data);
+	char* start = data;
+
+	char val[4];
+	memset(val, 0, sizeof(val));
+
+	/* BRD_DET*/
+	if (board_id == 0) {
+		if (get_str_val(start, "CH1_DET", '\n', val) == NULL)
+			quit(1, "%s: %s() failed to parse BOARD%d detect",
+				bitfury->drv->name, __func__, board_id + 1);
+
+		if (strcmp(val, "ON") == 0)
+			info->chipboard[board_id].detected = true;
+		else
+			info->chipboard[board_id].detected = false;
+	} else {
+		if (get_str_val(start, "CH2_DET", '\n', val) == NULL)
+			quit(1, "%s: %s() failed to parse BOARD%d detect",
+				bitfury->drv->name, __func__, board_id + 1);
+
+		if (strcmp(val, "ON") == 0)
+			info->chipboard[board_id].detected = true;
+		else
+			info->chipboard[board_id].detected = false;
+	}
+}
+
+static void parse_board_version(struct cgpu_info *bitfury, uint8_t board_id, char* data)
+{
+	struct bitfury16_info *info = (struct bitfury16_info *)(bitfury->device_data);
+
+	if (check_crc(data) == false)
+		quit(1, "%s: %s() invalid BOARD%d version data recieved: [%s]",
+				bitfury->drv->name, __func__, board_id + 1, data);
+
+	/* BRD */
+	char* start = data;
+	start = get_int_val(start, "BRD", ';', (int*)&info->chipboard[board_id].board_ver);
+	if (start == NULL)
+		quit(1, "%s: %s() failed to parse BOARD%d BRD",
+			bitfury->drv->name, __func__, board_id + 1);
+
+	/* FW */
+	start = get_int_val(start, "FW", ';', (int*)&info->chipboard[board_id].board_fwver);
+	if (start == NULL)
+		quit(1, "%s: %s() failed to parse BOARD%d FW",
+			bitfury->drv->name, __func__, board_id + 1);
+
+	/* ID */
+	if (get_str_val(start, "ID", '#', info->chipboard[board_id].board_hwid) == NULL)
+		quit(1, "%s: %s() failed to parse BOARD%d ID",
+			bitfury->drv->name, __func__, board_id + 1);
+}
+
+void get_board_info(struct cgpu_info *bitfury, uint8_t board_id)
+{
+	struct bitfury16_info *info = (struct bitfury16_info *)(bitfury->device_data);
+	char buff[256];
+
+	if (info->chipboard[board_id].detected == true) {
+		memset(buff, 0, sizeof(buff));
+		if (device_uart_txrx(board_id + 1, "V", buff) < 0)
+			quit(1, "%s: %s() failed to get BOARD%d version",
+				bitfury->drv->name, __func__, board_id + 1);
+
+		parse_board_version(bitfury, board_id, buff);
+
+		applog(LOG_INFO, "%s: BOARD%d version: [%d]", bitfury->drv->name,
+				board_id + 1, info->chipboard[board_id].board_ver);
+		applog(LOG_INFO, "%s: BOARD%d firmware version: [%d]", bitfury->drv->name,
+				board_id + 1, info->chipboard[board_id].board_fwver);
+		applog(LOG_INFO, "%s: BOARD%d hardware id: [%s]", bitfury->drv->name,
+				board_id + 1, info->chipboard[board_id].board_hwid);
+	}
+}
+
+int8_t parse_hwstats(struct bitfury16_info *info, uint8_t board_id, char* data)
+{
+	uint8_t i;
+	int32_t value;
+	char    strvalue[17];
+
+	if (check_crc(data) == false) {
+		applog(LOG_ERR, "BF16: invalid BOARD%d hwstats recieved: [%s]",
+				board_id + 1, data);
+
+		return -1;
+	}
+
+	/* T */
+	char* start = data;
+	start = get_int_val(start, "T", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].temp = value / 10.0;
+	else {
+		info->chipboard[board_id].temp = 0.0;
+		return -1;
+	}
+
+	/* UB */
+	start = get_int_val(start, "UB", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].u_board = value / 10.0;
+	else {
+		info->chipboard[board_id].u_board = 0.0;
+		return -1;
+	}
+
+	/* P1 */
+	start = get_int_val(start, "P1", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].p_chain1_enabled = value;
+	else {
+		info->chipboard[board_id].p_chain1_enabled = 0;
+		return -1;
+	}
+
+	/* P2 */
+	start = get_int_val(start, "P2", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].p_chain2_enabled = value;
+	else {
+		info->chipboard[board_id].p_chain2_enabled = 0;
+		return -1;
+	}
+
+	/* U1 */
+	start = get_int_val(start, "U1", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].u_chain1 = value / 10.0;
+	else {
+		info->chipboard[board_id].u_chain1 = 0.0;
+		return -1;
+	}
+
+	/* U2 */
+	start = get_int_val(start, "U2", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].u_chain2 = value / 10.0;
+	else {
+		info->chipboard[board_id].u_chain2 = 0.0;
+		return -1;
+	}
+
+	/* I1 */
+	start = get_int_val(start, "I1", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].i_chain1 = value / 10.0;
+	else {
+		info->chipboard[board_id].i_chain1 = 0.0;
+		return -1;
+	}
+
+	/* I2 */
+	start = get_int_val(start, "I2", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].i_chain2 = value / 10.0;
+	else {
+		info->chipboard[board_id].i_chain2 = 0.0;
+		return -1;
+	}
+
+	/* RPM */
+	start = get_int_val(start, "RPM", ';', (int *)&info->chipboard[board_id].rpm);
+	if (start == NULL) {
+		info->chipboard[board_id].rpm = 0;
+		return -1;
+	}
+
+	/* A - alarms - may be absent */
+	bool alarm = false;
+	for (i = 0; i < 3; i++) {
+		memset(strvalue, 0, sizeof(strvalue));
+		start = get_str_val(start, "A", ';', strvalue);
+		if (start == NULL)
+			return -1;
+
+		if (strcmp(strvalue, "T") == 0) {
+			info->chipboard[board_id].a_temp = 1;
+			alarm = true;
+		} else if (strcmp(strvalue, "I1") == 0) {
+			info->chipboard[board_id].a_ichain1 = 1;
+			alarm = true;
+		} else if (strcmp(strvalue, "I2") == 0) {
+			info->chipboard[board_id].a_ichain2 = 1;
+			alarm = true;
+		}
+	}
+
+	if (alarm == false) {
+		info->chipboard[board_id].a_temp    = 0;
+		info->chipboard[board_id].a_ichain1 = 0;
+		info->chipboard[board_id].a_ichain2 = 0;
+	}
+
+	/* F */
+	start = get_int_val(start, "F", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].fan_speed = value;
+	else {
+		info->chipboard[board_id].fan_speed = 0;
+		return -1;
+	}
+
+	/* AI */
+	start = get_int_val(start, "AI", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].i_alarm = value / 10.0;
+	else {
+		info->chipboard[board_id].i_alarm = 0.0;
+		return -1;
+	}
+
+	/* AT */
+	start = get_int_val(start, "AT", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].t_alarm = value / 10.0;
+	else {
+		info->chipboard[board_id].t_alarm = 0.0;
+		return -1;
+	}
+
+	/* AG */
+	start = get_int_val(start, "AG", ';', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].t_gisteresis = value / 10.0;
+	else {
+		info->chipboard[board_id].t_gisteresis = 0.0;
+		return -1;
+	}
+
+	/* FM */
+	memset(strvalue, 0, sizeof(strvalue));
+	start = get_str_val(start, "FM", ';', strvalue);
+	if (start != NULL)
+		info->chipboard[board_id].fan_mode = strvalue[0];
+	else
+		return -1;
+
+	/* TT */
+	start = get_int_val(start, "TT", '#', (int *)&value);
+	if (start != NULL)
+		info->chipboard[board_id].target_temp = value / 10.0;
+	else {
+		info->chipboard[board_id].target_temp = 0.0;
+		return -1;
+	}
+
+	return 0;
+}
+
+int8_t enable_power_chain(struct cgpu_info *bitfury, uint8_t board_id, uint8_t chain)
+{
+	struct bitfury16_info *info = (struct bitfury16_info *)(bitfury->device_data);
+
+	if (info->chipboard[board_id].detected == true) {
+		char uart_cmd[8];
+		memset(uart_cmd, 0, sizeof(uart_cmd));
+
+		switch (chain) {
+			case 0:
+				sprintf(uart_cmd, "P");
+				break;
+			case 1:
+			case 2:
+				sprintf(uart_cmd, "P:%d", chain);
+				break;
+		}
+
+		if (device_uart_transfer(board_id + 1, uart_cmd) < 0) {
+			switch (chain) {
+				case 0:
+					applog(LOG_ERR, "%s: failed to enable BOARD%d power chain",
+							bitfury->drv->name, board_id + 1);
+					break;
+				case 1:
+				case 2:
+					applog(LOG_ERR, "%s: failed to enable BOARD%d power chain %d",
+							bitfury->drv->name, board_id + 1, chain);
+					break;
+			}
+
+		} else {
+			switch (chain) {
+				case 0:
+					applog(LOG_NOTICE, "%s: enabled BOARD%d power chain",
+							bitfury->drv->name, board_id + 1);
+					break;
+				case 1:
+				case 2:
+					applog(LOG_NOTICE, "%s: enabled BOARD%d power chain %d",
+							bitfury->drv->name, board_id + 1, chain);
+					break;
+			}
+
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+int8_t disable_power_chain(struct cgpu_info *bitfury, uint8_t board_id, uint8_t chain)
+{
+	struct bitfury16_info *info = (struct bitfury16_info *)(bitfury->device_data);
+
+	if (info->chipboard[board_id].detected == true) {
+		char uart_cmd[8];
+		memset(uart_cmd, 0, sizeof(uart_cmd));
+
+		switch (chain) {
+			case 0:
+				sprintf(uart_cmd, "O");
+				break;
+			case 1:
+			case 2:
+				sprintf(uart_cmd, "O:%d", chain);
+				break;
+		}
+
+		if (device_uart_transfer(board_id + 1, uart_cmd) < 0) {
+			switch (chain) {
+				case 0:
+					applog(LOG_ERR, "%s: failed to disable BOARD%d power chain",
+							bitfury->drv->name, board_id + 1);
+					break;
+				case 1:
+				case 2:
+					applog(LOG_ERR, "%s: failed to disable BOARD%d power chain %d",
+							bitfury->drv->name, board_id + 1, chain);
+					break;
+			}
+
+		} else {
+			switch (chain) {
+				case 0:
+					applog(LOG_NOTICE, "%s: disabled BOARD%d power chain",
+							bitfury->drv->name, board_id + 1);
+					break;
+				case 1:
+				case 2:
+					applog(LOG_NOTICE, "%s: disabled BOARD%d power chain %d",
+							bitfury->drv->name, board_id + 1, chain);
+					break;
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+void led_red_enable(struct bitfury16_info *info)
+{
+	if (device_ctrl_transfer(SPI_CHANNEL1, 1, F_LED2) == 0) {
+		info->led_red_enabled = true;
+		gettimeofday(&info->led_red_switch, NULL);
+	}
+}
+
+void led_red_disable(struct bitfury16_info *info)
+{
+	if (device_ctrl_transfer(SPI_CHANNEL1, 0, F_LED2) == 0) {
+		info->led_red_enabled = false;
+		gettimeofday(&info->led_red_switch, NULL);
+	}
+}
+
+void led_green_enable(struct bitfury16_info *info)
+{
+	if (device_ctrl_transfer(SPI_CHANNEL1, 1, F_LED1) == 0) {
+		info->led_green_enabled = true;
+		gettimeofday(&info->led_green_switch, NULL);
+	}
+}
+
+void led_green_disable(struct bitfury16_info *info)
+{
+	if (device_ctrl_transfer(SPI_CHANNEL1, 0, F_LED1) == 0) {
+		info->led_green_enabled = false;
+		gettimeofday(&info->led_green_switch, NULL);
+	}
+}
+
+void buzzer_enable(struct bitfury16_info *info)
+{
+	if (device_ctrl_transfer(SPI_CHANNEL1, 1, F_BUZZER) == 0) {
+		info->buzzer_enabled = true;
+		gettimeofday(&info->buzzer_switch, NULL);
+	}
+}
+
+void buzzer_disable(struct bitfury16_info *info)
+{
+	if (device_ctrl_transfer(SPI_CHANNEL1, 0, F_BUZZER) == 0) {
+		info->buzzer_enabled = false;
+		gettimeofday(&info->buzzer_switch, NULL);
+	}
+}

+ 19 - 0
bf16-mspcontrol.h

@@ -0,0 +1,19 @@
+#ifndef BF16_MSPCONTROL_H
+#define BF16_MSPCONTROL_H
+
+#include "driver-bitfury16.h"
+
+void parse_board_detect(struct cgpu_info *bitfury, uint8_t board_id, char* data);
+void get_board_info(struct cgpu_info *bitfury, uint8_t board_id);
+int8_t parse_hwstats(struct bitfury16_info *info, uint8_t board_id, char* data);
+int8_t enable_power_chain(struct cgpu_info *bitfury, uint8_t board_id, uint8_t chain);
+int8_t disable_power_chain(struct cgpu_info *bitfury, uint8_t board_id, uint8_t chain);
+
+void led_red_enable(struct bitfury16_info *info);
+void led_red_disable(struct bitfury16_info *info);
+void led_green_enable(struct bitfury16_info *info);
+void led_green_disable(struct bitfury16_info *info);
+void buzzer_enable(struct bitfury16_info *info);
+void buzzer_disable(struct bitfury16_info *info);
+
+#endif /* BF16_MSPCONTROL_H */

+ 110 - 0
bf16-spidevice.c

@@ -0,0 +1,110 @@
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/spi/spidev.h>
+#include <unistd.h>
+
+#include "bf16-spidevice.h"
+#include "miner.h"
+
+char *spi0_device_name  = "/dev/spidev1.1";
+char *spi1_device_name  = "/dev/spidev2.1";
+
+int8_t spi_init(device_t* attr, spi_channel_id_t channel_id, int8_t mode, uint32_t speed, uint16_t size)
+{
+	switch (channel_id) {
+	case SPI_CHANNEL1:
+		attr->device = spi1_device_name;
+		break;
+	case SPI_CHANNEL2:
+		attr->device = spi0_device_name;
+		break;
+	}
+
+	attr->mode = mode;
+	attr->speed = speed;
+	attr->bits = 8;
+	attr->size = size;
+	attr->rx = malloc(size);
+	attr->tx = malloc(size);
+
+	int fd;
+	if ((fd = open(attr->device, O_RDWR)) < 0) {
+		applog(LOG_ERR, "BF16: %s() failed to open device [%s]: %s",
+				__func__, attr->device, strerror(errno));
+		return -1;
+	}
+
+	/* SPI mode */
+	if (ioctl(fd, SPI_IOC_WR_MODE, &(attr->mode)) < 0) {
+		applog(LOG_ERR, "BF16: %s() failed to set SPI mode: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if (ioctl(fd, SPI_IOC_RD_MODE, &(attr->mode)) < 0) {
+		applog(LOG_ERR, "BF16: %s() failed to get SPI mode: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	/* bits per word */
+	if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &(attr->bits)) < 0) {
+		applog(LOG_ERR, "BF16: %s() failed to set SPI bits per word: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &(attr->bits)) < 0) {
+		applog(LOG_ERR, "BF16: %s() failed to get SPI bits per word: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	/* max speed hz */
+	if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &(attr->speed)) < 0) {
+		applog(LOG_ERR, "BF16: %s() failed to set SPI max speed hz: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &(attr->speed)) < 0) {
+		applog(LOG_ERR, "BF16: %s() failed to get SPI max speed hz: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	attr->fd = fd;
+
+	return 0;
+}
+
+void spi_transfer(device_t *attr)
+{
+	struct spi_ioc_transfer tr = {
+		.tx_buf = (unsigned long) (attr->tx),
+		.rx_buf = (unsigned long) (attr->rx),
+		.len = attr->datalen,
+		.delay_usecs = attr->delay,
+		.speed_hz = attr->speed,
+		.bits_per_word = attr->bits
+	};
+
+	if (ioctl(attr->fd, SPI_IOC_MESSAGE(1), &tr) < 0)
+		quit(1, "BF16: %s() failed to send SPI message: %s", __func__, strerror(errno));
+
+#if 0
+	uint16_t i;
+	char data[2*4096];
+	memset(data, 0, sizeof(data));
+	for (i = 0; i < attr->datalen; i++)
+		sprintf(data, "%s%02x", data, attr->tx[i]);
+	applog(LOG_DEBUG, "BF16: TX -> [%s]", data);
+
+	memset(data, 0, sizeof(data));
+	for (i = 0; i < attr->datalen; i++)
+		sprintf(data, "%s%02x", data, attr->rx[i]);
+	applog(LOG_DEBUG, "BF16: RX <- [%s]", data);
+#endif
+
+}
+
+void spi_release(device_t *attr)
+{
+	free(attr->rx);
+	free(attr->tx);
+	close(attr->fd);
+}

+ 22 - 0
bf16-spidevice.h

@@ -0,0 +1,22 @@
+#ifndef BF16_SPIDEVICE_H
+#define BF16_SPIDEVICE_H
+
+#include "bf16-device.h"
+
+#define SPI_BUFFER_SIZE  4096
+#define SPI_SPEED        20000000
+
+typedef enum {
+	SPI_CHANNEL1 = 1,
+	SPI_CHANNEL2
+} spi_channel_id_t;
+
+extern char *spi0_device_name;
+extern char *spi1_device_name;
+
+int8_t spi_init(device_t* attr, spi_channel_id_t channel_id,
+		int8_t mode, uint32_t speed, uint16_t size);
+void spi_transfer(device_t *attr);
+void spi_release(device_t *attr);
+
+#endif /* BF16_SPIDEVICE_H */

+ 149 - 0
bf16-uartdevice.c

@@ -0,0 +1,149 @@
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "bf16-uartdevice.h"
+#include "miner.h"
+
+#define SRV_TIMEOUT 2
+
+char *uart1_device_name = "/dev/ttyO1";
+char *uart2_device_name = "/dev/ttyO4";
+
+int8_t uart_init(device_t* attr, uart_channel_id_t channel_id, int8_t mode, uint32_t speed, uint16_t size)
+{
+	switch (channel_id) {
+	case UART_CHANNEL1:
+		attr->device = uart1_device_name;
+		break;
+	case UART_CHANNEL2:
+		attr->device = uart2_device_name;
+		break;
+	}
+
+	attr->mode = mode;
+	attr->speed = speed;
+	attr->bits = 8;
+	attr->size = size;
+	attr->rx = malloc(size);
+	attr->tx = malloc(size);
+
+	int fd;
+	if ((fd = open(attr->device, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
+		quit(1, "BF16: %s() failed to open device [%s]: %s",
+				__func__, attr->device, strerror(errno));
+
+	attr->fd = fd;
+
+	if (fcntl(fd, F_SETFL, 0) < 0)
+		quit(1, "BF16: %s() failed to set descriptor status flags [%s]: %s",
+				__func__, attr->device, strerror(errno));
+
+	struct termios settings;
+	if (tcgetattr(fd, &settings) < 0)
+		quit(1, "BF16: %s() failed to get device attributes [%s]: %s",
+				__func__, attr->device, strerror(errno));
+
+	settings.c_cflag &= ~PARENB; /* no parity */
+	settings.c_cflag &= ~CSTOPB; /* 1 stop bit */
+	settings.c_cflag &= ~CSIZE;
+	settings.c_cflag |= CS8 | CLOCAL | CREAD; /* 8 bits */
+	settings.c_cc[VMIN] = 1;
+	settings.c_cc[VTIME] = 2;
+	settings.c_lflag = ICANON; /* canonical mode */
+	settings.c_oflag &= ~OPOST; /* raw output */
+
+	if (cfsetospeed(&settings, speed) < 0)
+		quit(1, "BF16: %s() failed to set device output speed [%s]: %s",
+				__func__, attr->device, strerror(errno));
+
+	if (cfsetispeed(&settings, speed) < 0)
+		quit(1, "BF16: %s() failed to set device input speed [%s]: %s",
+				__func__, attr->device, strerror(errno));
+
+	if (tcsetattr(fd, TCSANOW, &settings) < 0)
+		quit(1, "BF16: %s() failed to get device attributes [%s]: %s",
+				__func__, attr->device, strerror(errno));
+
+	if (tcflush(fd, TCOFLUSH) < 0)
+		quit(1, "BF16: %s() failed to flush device data [%s]: %s",
+				__func__, attr->device, strerror(errno));
+
+	return 0;
+}
+
+/* TODO: add errors processing */
+static int read_to(int fd, uint8_t* buffer, int len, int timeout)
+{
+	fd_set readset;
+	int result;
+	struct timeval tv;
+
+	FD_ZERO(&readset);
+	FD_SET(fd, &readset);
+
+	tv.tv_sec = timeout;
+	tv.tv_usec = 0;
+	result = select(fd + 1, &readset, NULL, NULL, &tv);
+ 
+	if (result < 0)
+		return result;
+	else if (result > 0 && FD_ISSET(fd, &readset)) {
+		result = read(fd, buffer, len);
+		return result;
+	}
+	return -2;
+}
+
+static int write_to(int fd, uint8_t* buffer, int len, int timeout)
+{
+	fd_set writeset;
+	int result;
+	struct timeval tv;
+
+	FD_ZERO(&writeset);
+	FD_SET(fd, &writeset);
+
+	tv.tv_sec = timeout;
+	tv.tv_usec = 0;
+	result = select(fd + 1, NULL, &writeset, NULL, &tv);
+
+	if (result < 0)
+		return result;
+	else if (result > 0 && FD_ISSET(fd, &writeset)) {
+		result = write(fd, buffer, len);
+		return result;
+	}
+
+	return -2;
+}
+
+int8_t uart_transfer(device_t *attr)
+{
+	int ret;
+
+	if ((ret = write_to(attr->fd, attr->tx, attr->datalen, SRV_TIMEOUT)) < 1) {
+		applog(LOG_ERR, "BF16: %s() failed to send UART message to [%s]: %s",
+				__func__, attr->device, strerror(errno));
+
+		attr->datalen = 0;
+		return -1;
+	}
+
+	if ((ret = read_to(attr->fd, attr->rx, attr->size, SRV_TIMEOUT)) < 1) {
+		applog(LOG_ERR, "BF16: %s() failed to read UART message from [%s]: %s",
+				__func__, attr->device, strerror(errno));
+
+		attr->datalen = 0;
+		return -1;
+	}
+
+	attr->datalen = ret;
+	return 0;
+}
+
+void uart_release(device_t *attr)
+{
+	free(attr->rx);
+	free(attr->tx);
+	close(attr->fd);
+}

+ 23 - 0
bf16-uartdevice.h

@@ -0,0 +1,23 @@
+#ifndef BF16_UARTDEVICE_H
+#define BF16_UARTDEVICE_H
+
+#include <termios.h>
+
+#include "bf16-device.h"
+
+#define UART_BUFFER_SIZE 128
+
+typedef enum {
+	UART_CHANNEL1 = 1,
+	UART_CHANNEL2
+} uart_channel_id_t;
+
+extern char *uart1_device_name;
+extern char *uart2_device_name;
+
+int8_t uart_init(device_t* attr, uart_channel_id_t channel_id,
+		int8_t mode, uint32_t speed, uint16_t size);
+int8_t uart_transfer(device_t *attr);
+void uart_release(device_t *attr);
+
+#endif /* BF16_UARTDEVICE_H */

+ 108 - 0
bitforce-firmware-flash.c

@@ -0,0 +1,108 @@
+/*
+ * Copyright 2012 Luke Dashjr
+ *
+ * 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.
+ */
+
+#define _BSD_SOURCE
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libgen.h>
+#include <arpa/inet.h>
+
+#define BFL_FILE_MAGIC   "BFLDATA"
+#define BFL_UPLOAD_MAGIC "NGH-STREAM"
+
+#define myassert(expr, n, ...) \
+do {  \
+	if (!(expr)) {  \
+		fprintf(stderr, __VA_ARGS__);  \
+		return n;  \
+	}  \
+} while(0)
+
+#define ERRRESP(buf)  buf, (buf[strlen(buf)-1] == '\n' ? "" : "\n")
+
+#define WAITFOROK(n, msg) \
+do {  \
+	myassert(fgets(buf, sizeof(buf), BFL), n, "Error reading response from " msg "\n");  \
+	myassert(!strcmp(buf, "OK\n"), n, "Invalid response from " msg ": %s%s", ERRRESP(buf));  \
+} while(0)
+
+int main(int argc, char**argv)
+{
+	myassert(argc == 3, 1, "Usage: %s <serialdev> <firmware.bfl>\n", argv[0]);
+	setbuf(stdout, NULL);
+	
+	// Check filename
+	char *FWname = basename(strdup(argv[2]));
+	size_t FWnameLen = strlen(FWname);
+	myassert(FWnameLen <= 255, 0x0f, "Firmware filename '%s' is too long\n", FWname);
+	uint8_t n8 = FWnameLen;
+	
+	// Open and check firmware file
+	FILE *FW = fopen(argv[2], "r");
+	myassert(FW, 0x10, "Failed to open '%s' for reading\n", argv[2]);
+	char buf[0x20];
+	myassert(1 == fread(buf, 7, 1, FW), 0x10, "Failed to read from '%s'\n", argv[2]);
+	myassert(!memcmp(buf, BFL_FILE_MAGIC, sizeof(BFL_FILE_MAGIC)-1), 0x11, "'%s' doesn't look like a BFL firmware\n", argv[2]);
+	myassert(!fseek(FW, 0, SEEK_END), 0x12, "Failed to find end of '%s'\n", argv[2]);
+	long FWlen = ftell(FW);
+	myassert(FWlen > 0, 0x12, "Couldn't get size of '%s'\n", argv[2]);
+	myassert(!fseek(FW, 7, SEEK_SET), 0x12, "Failed to rewind firmware file after getting size\n");
+	FWlen -= 7;
+	printf("Firmware file looks OK :)\n");
+	
+	// Open device
+	FILE *BFL = fopen(argv[1], "r+");
+	myassert(BFL, 0x20, "Failed to open '%s' for read/write\n", argv[1]);
+	myassert(!setvbuf(BFL, NULL, _IOFBF, 1032), 0x21, "Failed to setup buffer for device");
+	
+	// ZAX: Start firmware upload
+	printf("Starting firmware upload... ");
+	myassert(1 == fwrite("ZAX", 3, 1, BFL), 0x22, "Failed to issue ZAX command\n");
+	WAITFOROK(0x22, "ZAX");
+	
+	// Firmware upload header
+	myassert(1 == fwrite(BFL_UPLOAD_MAGIC, sizeof(BFL_UPLOAD_MAGIC)-1, 1, BFL), 0x23, "Failed to send firmware upload header (magic)\n");
+	uint32_t n32 = htonl(FWlen - FWlen / 6);
+	myassert(1 == fwrite(&n32, sizeof(n32), 1, BFL), 0x23, "Failed to send firmware upload header (size)\n");
+	myassert(1 == fwrite("\0\0", 2        , 1, BFL), 0x23, "Failed to send firmware upload header (padding 1)\n");
+	myassert(1 == fwrite(&n8, sizeof(n8)  , 1, BFL), 0x23, "Failed to send firmware upload header (filename length)\n");
+	myassert(1 == fwrite(FWname, n8       , 1, BFL), 0x23, "Failed to send firmware upload header (filename)\n");
+	myassert(1 == fwrite("\0>>>>>>>>", 9  , 1, BFL), 0x23, "Failed to send firmware upload header (padding 2)\n");
+	WAITFOROK(0x23, "firmware upload header");
+	printf("OK, sending...\n");
+	
+	// Actual firmware upload
+	long i, j;
+	for (i = 0, j = 0; i < FWlen; ++i) {
+		myassert(1 == fread(&n8, sizeof(n8), 1, FW), 0x30, "Error reading data from firmware file\n");
+		if (5 == i % 6)
+			continue;
+		n8 ^= 0x2f;
+		myassert(1 == fwrite(&n8, sizeof(n8), 1, BFL), 0x31, "Error sending data to device\n");
+		if (!(++j % 0x400)) {
+			myassert(1 == fwrite(">>>>>>>>", 8, 1, BFL), 0x32, "Error sending block-finish to device\n");
+			printf("\r%5.2f%% complete", (double)i * 100. / (double)FWlen);
+			WAITFOROK(0x32, "block-finish");
+		}
+	}
+	printf("\r100%% complete :)\n");
+	myassert(1 == fwrite(">>>>>>>>", 8, 1, BFL), 0x3f, "Error sending upload-finished to device\n");
+	myassert(fgets(buf, sizeof(buf), BFL), 0x3f, "Error reading response from upload-finished\n");
+	myassert(!strcmp(buf, "DONE\n"), 0x3f, "Invalid response from upload-finished: %s%s", ERRRESP(buf));
+
+	// ZBX: Finish programming
+	printf("Waiting for device... ");
+	myassert(1 == fwrite("ZBX", 3, 1, BFL), 0x40, "Failed to issue ZBX command\n");
+	WAITFOROK(0x40, "ZBX");
+	printf("All done! Try mining to test the flash succeeded.\n");
+
+	return 0;
+}

+ 2310 - 0
bitmain-board-test.c

@@ -0,0 +1,2310 @@
+/*
+ * Copyright 2016-2017 Fazio Bai <yang.bai@bitmain.com>
+ * Copyright 2016-2017 Clement Duan <kai.duan@bitmain.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+#include "config.h"
+#include <assert.h>
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <math.h>
+
+#ifndef WIN32
+#include <sys/select.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+#else
+#include "compat.h"
+#include <windows.h>
+#include <io.h>
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <string.h>
+
+#include <zlib.h>
+
+#include "elist.h"
+#include "miner.h"
+
+
+#include "util.h"
+#include "driver-btm-soc.h"
+
+#include "bitmain-board-test.h"
+
+// below are defined in driver-btm-c5.c
+extern pthread_mutex_t iic_mutex;
+extern bool isChainAllCoresOpened[BITMAIN_MAX_CHAIN_NUM];
+extern bool someBoardUpVoltage;
+extern int lowest_testOK_temp[BITMAIN_MAX_CHAIN_NUM];
+extern int LOWEST_TEMP_DOWN_FAN;
+extern int chain_badcore_num[BITMAIN_MAX_CHAIN_NUM][256];
+extern pthread_mutex_t opencore_readtemp_mutex;
+extern unsigned int *axi_fpga_addr;             // defined in driver-btm-c5.c
+extern unsigned int *fpga_mem_addr;             //defined in driver-btm-c5.c
+extern int fd_fpga_mem;                                // fpga memory
+extern unsigned int *nonce2_jobid_address;      // the value should be filled in NONCE2_AND_JOBID_STORE_ADDRESS
+
+extern void open_core_one_chain(int chainIndex, bool nullwork_enable);
+extern void insert_reg_data(unsigned int *buf);
+extern int GetTotalRate();
+extern int getVoltageLimitedFromHashrate(int hashrate_GHz);
+extern bool isChainEnough();
+extern void set_PWM(unsigned char pwm_percent);
+extern int get_nonce_number_in_fifo(void);
+extern int get_return_nonce(unsigned int *buf);
+extern int get_nonce_fifo_interrupt(void);
+extern void set_nonce_fifo_interrupt(unsigned int value);
+extern void set_TW_write_command(unsigned int *value);
+extern void set_TW_write_command_vil(unsigned int *value);
+extern int get_buffer_space(void);
+extern int get_freqvalue_by_index(int index);
+extern int getChainAsicFreqIndex(int chainIndex, int asicIndex);
+extern int get_hash_on_plug(void);
+
+
+////////// below is only used inside of this file !!! so all static!/////////////
+static bool chain_need_opencore[BITMAIN_MAX_CHAIN_NUM]= {false};
+static bool StartSendFlag[BITMAIN_MAX_CHAIN_NUM];
+
+static int chain_DataCount[BITMAIN_MAX_CHAIN_NUM];
+static int chain_ValidNonce[BITMAIN_MAX_CHAIN_NUM];
+static int chain_PassCount[BITMAIN_MAX_CHAIN_NUM];
+
+static int chain_vol_value[BITMAIN_MAX_CHAIN_NUM];  // the searching vol
+static int chain_vol_final[BITMAIN_MAX_CHAIN_NUM];  // the final vol, need saved in PIC
+static int chain_vol_added[BITMAIN_MAX_CHAIN_NUM];  // how many vol added , recorded in PIC
+
+static int last_result[BITMAIN_MAX_CHAIN_NUM][256];
+static int last_result_opencore[BITMAIN_MAX_CHAIN_NUM][256];
+
+static int result = 0;
+static bool search_freq_result[BITMAIN_MAX_CHAIN_NUM];  // set true as default
+
+static struct testpatten_cgpu_info cgpu;
+static volatile bool gBegin_get_nonce = false;
+
+static unsigned int send_work_num[BITMAIN_MAX_CHAIN_NUM];
+
+static int asic_nonce_num[BITMAIN_MAX_CHAIN_NUM][256];
+static int asic_core_nonce_num[BITMAIN_MAX_CHAIN_NUM][256][256];  // 1st: which asic, 2nd: which core
+static int last_nonce_num[BITMAIN_MAX_CHAIN_NUM];
+static int repeated_nonce_num[BITMAIN_MAX_CHAIN_NUM];
+static uint32_t repeated_nonce_id[BITMAIN_MAX_CHAIN_NUM][256];
+static int valid_nonce_num[BITMAIN_MAX_CHAIN_NUM];    // all the received nonce in one test
+static int err_nonce_num[BITMAIN_MAX_CHAIN_NUM];
+static int total_valid_nonce_num=0;
+
+static volatile bool start_receive = false;
+
+static int testModeOKCounter[BITMAIN_MAX_CHAIN_NUM];
+
+static struct configuration Conf;  //store information that read from Config.ini
+static struct _CONFIG conf;        //store the information that handled from Config.ini
+
+static bool ExitFlag=false;
+static bool receiveExit;
+static bool sendExit[BITMAIN_MAX_CHAIN_NUM];
+
+static void writeLogFile(char *logstr);
+static int calculate_asic_number(unsigned int actual_asic_number);
+static int calculate_core_number(unsigned int actual_core_number);
+
+
+#define CONFIG_FILE "/etc/config/Config.ini"
+#define FORCE_FREQ_FILE "/etc/config/forcefreq.txt"
+#define LAST_FORCE_FREQ_FILE    "/etc/config/last_forcefreq.txt"
+
+static bool last_all_pass(int chainIndex)
+{
+    int i = 0;
+    for(i=0; i<CHAIN_ASIC_NUM; i++)
+        if (!last_result[chainIndex][i])
+            return false;
+    return true;
+}
+
+static bool last_all_core_opened(int chainIndex)
+{
+    int i = 0;
+    for(i=0; i<CHAIN_ASIC_NUM; i++)
+        if (!last_result_opencore[chainIndex][i])
+            return false;
+    return true;
+}
+
+static bool isAllChainChipCoreOpened()
+{
+    int i;
+    FOR_LOOP_CHAIN
+    {
+        if(cgpu.chain_exist[i]==0)
+            continue;
+
+        if(!last_all_core_opened(i))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static int load_testpatten_work(int id, int count)
+{
+    struct testpatten_work * new_work;
+    int subid = 0;
+    unsigned long DataLen=MAX_WORK*48;  // midstate + data + nonce = 48 bytes
+    unsigned char *workData;
+    unsigned char *zipData;
+    unsigned long zipLen;
+
+    workData=(unsigned char *)malloc(DataLen);
+
+    fseek(cgpu.fps[id],0,SEEK_END);
+    zipLen = ftell(cgpu.fps[id]);
+    fseek(cgpu.fps[id],0,SEEK_SET);
+
+    zipData=(unsigned char *)malloc(zipLen);
+    zipLen=fread(zipData,1,zipLen,cgpu.fps[id]);
+
+    uncompress(workData,&DataLen,zipData,zipLen);
+    free(zipData);
+
+    cgpu.works[id] = (struct testpatten_work *)malloc(count * sizeof(struct testpatten_work));
+    if(NULL == cgpu.works[id])
+    {
+        applog(LOG_ERR, "malloc struct testpatten_work err\n");
+        return 0;
+    }
+
+    while(subid*48<DataLen)
+    {
+        if(subid >= count)
+            break;
+
+        new_work = cgpu.works[id] + subid;
+
+        memcpy((uint8_t *)(&new_work->nonce) ,workData+subid*48+44, 4);
+        new_work->nonce = htonl(new_work->nonce);
+
+        memcpy(new_work->midstate ,workData+subid*48, 32);
+        memcpy(new_work->data ,workData+subid*48+32, 12);
+
+        new_work->id = subid;
+        subid++;
+    }
+    free(workData);
+    return subid;
+}
+
+static int read_config()
+{
+    FILE * file;
+    int forceFreq,forceFlag;
+    struct configuration *m_conf = &Conf;
+    char str[1024] = {0};
+    char * temp;
+    int offset = 0, starttemp = 0;
+    int i;
+    file = fopen(CONFIG_FILE, "r");
+    char logstr[1024];
+
+    while(fgets(str, sizeof(str) - 1 , file))
+    {
+        if(str[0] == '#' || str[1] == '#')
+            continue;
+
+        if((temp = strstr(str, "TestDir="))!=NULL)
+        {
+            temp += 8;
+            for(i = 0; i < 64; i++)
+            {
+                cgpu.workdataPathPrefix[i] = *temp++;
+                //printf("%c", *temp);
+                if(*temp == '\n' || *temp == '\r')
+                    break;
+            }
+            i++;
+            cgpu.workdataPathPrefix[i] = '\0';
+            printf("workdataPathPrefix:%s\n", cgpu.workdataPathPrefix);
+        }
+        else if((temp = strstr(str, "DataCount="))!=NULL)
+        {
+            temp += 10;
+            sscanf(temp, "%d", &m_conf->DataCount);
+        }
+        else if((temp = strstr(str, "PassCount1="))!=NULL)
+        {
+            temp += 11;
+            sscanf(temp, "%d", &m_conf->PassCount1);
+        }
+        else if((temp = strstr(str, "PassCount2="))!=NULL)
+        {
+            temp += 11;
+            sscanf(temp, "%d", &m_conf->PassCount2);
+        }
+        else if((temp = strstr(str, "PassCount3="))!=NULL)
+        {
+            temp += 11;
+            sscanf(temp, "%d", &m_conf->PassCount3);
+        }
+        else if((temp = strstr(str, "Freq="))!=NULL)
+        {
+            temp += 5;
+            sscanf(temp, "%d", &m_conf->Freq);
+
+            m_conf->force_freq=0;
+        }
+        else if((temp = strstr(str, "freq_e="))!=NULL)
+        {
+            temp += 7;
+            sscanf(temp, "%d", &m_conf->freq_e);
+        }
+        else if((temp = strstr(str, "UseConfigVol="))!=NULL)
+        {
+            temp += 13;
+            sscanf(temp, "%d", &m_conf->UseConfigVol);
+        }
+        else if((temp = strstr(str, "freq_m="))!=NULL)
+        {
+            temp += 7;
+            sscanf(temp, "%d", &m_conf->freq_m);
+        }
+        else if((temp = strstr(str, "freq_a="))!=NULL)
+        {
+            temp += 7;
+            sscanf(temp, "%d", &m_conf->freq_a);
+        }
+        else if((temp = strstr(str, "freq_t="))!=NULL)
+        {
+            temp += 7;
+            sscanf(temp, "%d", &m_conf->freq_t);
+        }
+        else if((temp = strstr(str, "force_freq="))!=NULL)
+        {
+            temp += 11;
+            //    sscanf(temp, "%d", &m_conf->force_freq);
+        }
+        else if((temp = strstr(str, "Timeout="))!=NULL)
+        {
+            temp +=8;
+            sscanf(temp, "%d", &m_conf->Timeout);
+        }
+        else if((temp = strstr(str, "UseFreqPIC="))!=NULL)
+        {
+            temp += 11;
+            sscanf(temp , "%d", &m_conf->UseFreqPIC);
+        }
+        else if((temp = strstr(str, "TestMode="))!=NULL)
+        {
+            temp += 9;
+            sscanf(temp, "%d", &m_conf->TestMode);
+        }
+        else if((temp = strstr(str, "CheckChain="))!=NULL)
+        {
+            temp += 11;
+            sscanf(temp, "%d", &m_conf->CheckChain);
+        }
+        else if((temp = strstr(str, "CommandMode="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%d", &m_conf->CommandMode);
+        }
+        else if((temp = strstr(str, "ValidNonce1="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%d", &m_conf->ValidNonce1);
+        }
+        else if((temp = strstr(str, "ValidNonce2="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%d", &m_conf->ValidNonce2);
+        }
+        else if((temp = strstr(str, "ValidNonce3="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%d", &m_conf->ValidNonce3);
+        }
+        else if((temp = strstr(str, "Pic_VOLTAGE="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%d", &m_conf->Pic);
+        }
+        else if((temp = strstr(str, "Voltage1="))!=NULL)
+        {
+            temp += 9;
+            sscanf(temp, "%d", &m_conf->Voltage1);
+        }
+        else if((temp = strstr(str, "Voltage2="))!=NULL)
+        {
+            temp += 9;
+            sscanf(temp, "%d", &m_conf->Voltage2);
+        }
+        else if((temp = strstr(str, "Voltage3="))!=NULL)
+        {
+            temp += 9;
+            sscanf(temp, "%d", &m_conf->Voltage3);
+        }
+        else if((temp = strstr(str, "final_voltage1="))!=NULL)
+        {
+            temp += 15;
+            sscanf(temp, "%ud", &m_conf->final_voltage1);
+        }
+        else if((temp = strstr(str, "final_voltage2="))!=NULL)
+        {
+            temp += 15;
+            sscanf(temp, "%ud", &m_conf->final_voltage2);
+        }
+        else if((temp = strstr(str, "final_voltage3="))!=NULL)
+        {
+            temp += 15;
+            sscanf(temp, "%ud", &m_conf->final_voltage3);
+        }
+        else if((temp = strstr(str, "freq_gap="))!=NULL)
+        {
+            temp += 9;
+            sscanf(temp, "%ud", &m_conf->freq_gap);
+        }
+        else if((temp = strstr(str, "OpenCoreGap="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%d", &m_conf->OpenCoreGap);
+        }
+        else if((temp = strstr(str, "CheckTemp="))!=NULL)
+        {
+            temp += 10;
+            sscanf(temp, "%d", &m_conf->checktemp);
+        }
+        else if((temp = strstr(str, "IICPic="))!=NULL)
+        {
+            temp += 7;
+            sscanf(temp, "%d", &m_conf->IICPic);
+        }
+        else if((temp = strstr(str, "Open_Core_Num1="))!=NULL)
+        {
+            temp += 15;
+            sscanf(temp, "%ud", &m_conf->OpenCoreNum1);
+        }
+        else if((temp = strstr(str, "Open_Core_Num2="))!=NULL)
+        {
+            temp += 15;
+            sscanf(temp, "%ud", &m_conf->OpenCoreNum2);
+        }
+        else if((temp = strstr(str, "Open_Core_Num3="))!=NULL)
+        {
+            temp += 15;
+            sscanf(temp, "%ud", &m_conf->OpenCoreNum3);
+        }
+        else if((temp = strstr(str, "Open_Core_Num4="))!=NULL)
+        {
+            temp += 15;
+            sscanf(temp, "%ud", &m_conf->OpenCoreNum4);
+        }
+        else if((temp = strstr(str, "DAC="))!=NULL)
+        {
+            temp += 4;
+            sscanf(temp, "%ud", &m_conf->dac);
+        }
+        else if((temp = strstr(str, "GetTempFrom="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%ud", &m_conf->GetTempFrom);
+        }
+        else if((temp = strstr(str, "TempSel="))!=NULL)
+        {
+            temp += 8;
+            sscanf(temp, "%ud", &m_conf->TempSel);
+        }
+        else if((temp = strstr(str, "TempSensor1="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%ud", &m_conf->TempSensor1);
+        }
+        else if((temp = strstr(str, "TempSensor2="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%ud", &m_conf->TempSensor2);
+        }
+        else if((temp = strstr(str, "TempSensor3="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%ud", &m_conf->TempSensor3);
+        }
+        else if((temp = strstr(str, "TempSensor4="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%ud", &m_conf->TempSensor4);
+        }
+        else if((temp = strstr(str, "DefaultTempOffset="))!=NULL)
+        {
+            temp += 18;
+            sscanf(temp, "%d", &offset);
+            if(offset < 0)
+            {
+                offset -= 2*offset;
+                m_conf->DefaultTempOffset = (signed char)offset;
+                m_conf->DefaultTempOffset -= 2*m_conf->DefaultTempOffset;
+                //printf("~~~~~~~~~ m_conf->DefaultTempOffset = %d\n", m_conf->DefaultTempOffset);
+            }
+            else
+            {
+                m_conf->DefaultTempOffset = offset;
+                //printf("~~~~~~~~~ m_conf->DefaultTempOffset = %d\n", m_conf->DefaultTempOffset);
+            }
+        }
+        else if((temp = strstr(str, "year="))!=NULL)
+        {
+            temp += 5;
+            sscanf(temp, "%d", &m_conf->year);
+            //printf("year = %d\n", m_conf->year);
+        }
+        else if((temp = strstr(str, "month="))!=NULL)
+        {
+            temp += 6;
+            sscanf(temp, "%d", &m_conf->month);
+        }
+        else if((temp = strstr(str, "date="))!=NULL)
+        {
+            temp += 5;
+            sscanf(temp, "%d", &m_conf->date);
+        }
+        else if((temp = strstr(str, "hour="))!=NULL)
+        {
+            temp += 5;
+            sscanf(temp, "%d", &m_conf->hour);
+        }
+        else if((temp = strstr(str, "minute="))!=NULL)
+        {
+            temp += 7;
+            sscanf(temp, "%d", &m_conf->minute);
+        }
+        else if((temp = strstr(str, "second="))!=NULL)
+        {
+            temp += 7;
+            sscanf(temp, "%d", &m_conf->second);
+        }
+        else if((temp = strstr(str, "StartSensor="))!=NULL)
+        {
+            temp += 12;
+            sscanf(temp, "%d", &m_conf->StartSensor);
+        }
+        else if((temp = strstr(str, "StartTemp="))!=NULL)
+        {
+            temp += 10;
+            sscanf(temp, "%d", &m_conf->StartTemp);
+            sscanf(temp, "%d", &starttemp);
+            if(starttemp < 0)
+            {
+                starttemp -= 2*starttemp;
+                m_conf->StartTemp = (signed char)starttemp;
+                m_conf->StartTemp -= 2*m_conf->StartTemp;
+                //printf("~~~~~~~~~ m_conf->DefaultTempOffset = %d\n", m_conf->DefaultTempOffset);
+            }
+            else
+            {
+                m_conf->StartTemp = starttemp;
+                //printf("~~~~~~~~~ m_conf->DefaultTempOffset = %d\n", m_conf->DefaultTempOffset);
+            }
+        }
+    }
+
+    m_conf->AsicNum=CHAIN_ASIC_NUM;
+    m_conf->AsicType=ASIC_TYPE;
+    m_conf->CoreNum=ASIC_CORE_NUM;
+    return 0;
+}
+
+static int process_config()
+{
+    uint32_t rBaudrate;
+    int temp_corenum = 0;
+
+    conf.CommandMode = Conf.CommandMode;
+
+    conf.TempSel = Conf.TempSel;
+
+    conf.GetTempFrom = Conf.GetTempFrom;
+
+    if(Conf.CommandMode == FIL)
+    {
+        if(conf.GetTempFrom == 1)   // read temp from asic
+        {
+            applog(LOG_ERR, "Can't get temperature from ASIC in FIL mode!\n");
+            return -1;
+        }
+    }
+
+    if(Conf.CommandMode == VIL)
+    {
+        if(conf.GetTempFrom == 1)   // read temp from asic
+        {
+            cgpu.temp_sel = Conf.TempSel;
+            cgpu.rfs = 1;
+            cgpu.tfs = 3;
+            //printf("cgpu.temp_sel = %d, cgpu.rfs = %d, cgpu.tfs = %d\n", cgpu.temp_sel, cgpu.rfs, cgpu.tfs);
+
+            if(Conf.TempSensor1 + Conf.TempSensor2 + Conf.TempSensor3 + Conf.TempSensor4)
+            {
+                conf.TempSensor1 = Conf.TempSensor1;
+                conf.TempSensor2 = Conf.TempSensor2;
+                conf.TempSensor3 = Conf.TempSensor3;
+                conf.TempSensor4 = Conf.TempSensor4;
+                conf.DefaultTempOffset = Conf.DefaultTempOffset;
+                cgpu.T1_offset_value = Conf.DefaultTempOffset;
+                cgpu.T2_offset_value = Conf.DefaultTempOffset;
+                cgpu.T3_offset_value = Conf.DefaultTempOffset;
+                cgpu.T4_offset_value = Conf.DefaultTempOffset;
+                conf.StartSensor = Conf.StartSensor;
+                conf.StartTemp = Conf.StartTemp;
+            }
+            else
+            {
+                applog(LOG_ERR, "Must set temperature sensor address!\n");
+                return -1;
+            }
+        }
+    }
+
+    conf.AsicType = ASIC_TYPE;
+
+    conf.core = Conf.CoreNum;
+
+    conf.freq_e = Conf.freq_e;
+
+    conf.freq_m = Conf.freq_m;
+
+    conf.freq_a = Conf.freq_a;
+
+    conf.freq_t = Conf.freq_t;
+
+    conf.force_freq = Conf.force_freq;
+
+    conf.UseConfigVol = Conf.UseConfigVol;
+
+    conf.OpenCoreNum1 = Conf.OpenCoreNum1;
+
+    conf.OpenCoreNum2 = Conf.OpenCoreNum2;
+
+    conf.OpenCoreNum3 = Conf.OpenCoreNum3;
+
+    conf.OpenCoreNum4 = Conf.OpenCoreNum4;
+
+    conf.asicNum = calculate_asic_number(CHAIN_ASIC_NUM);
+
+    conf.addrInterval = Conf.AddrInterval = CHIP_ADDR_INTERVAL;
+
+    temp_corenum = calculate_core_number(conf.core);
+
+    conf.testMode = Conf.TestMode;
+
+    conf.ValidNonce1 = Conf.ValidNonce1;
+
+    conf.ValidNonce2 = Conf.ValidNonce2;
+
+    conf.ValidNonce3 = Conf.ValidNonce3;
+
+    conf.Pic = Conf.Pic;
+
+    conf.IICPic = Conf.IICPic;
+
+    conf.dac= Conf.dac;
+
+    conf.Voltage1 = Conf.Voltage1;
+
+    conf.Voltage2 = Conf.Voltage2;
+
+    conf.Voltage3 = Conf.Voltage3;
+
+    conf.OpenCoreGap = Conf.OpenCoreGap;
+
+    conf.checktemp = Conf.checktemp;
+
+    if(ASIC_TYPE==1385 || ASIC_TYPE == 1387)
+    {
+        conf.freq = Conf.Freq;
+    }
+    else
+    {
+        printf("%s: ASIC_TYPE = %d, but it is not correct!\n", __FUNCTION__, ASIC_TYPE);
+    }
+
+    conf.year = Conf.year;
+    conf.month = Conf.month;
+    conf.date = Conf.date;
+    conf.hour = Conf.hour;
+    conf.minute = Conf.minute;
+    conf.second = Conf.second;
+
+    if(Conf.Timeout <= 0)
+        conf.timeout = 0x1000000/temp_corenum*conf.addrInterval/Conf.Freq*95/100;
+    else
+        conf.timeout = Conf.Timeout;
+
+    rBaudrate = 1000000 * 5/3 / conf.timeout * (64*8);//64*8 need send bit, ratio=2/3
+    conf.baud = 25000000/rBaudrate/8 - 1;
+    if(conf.baud > DEFAULT_BAUD_VALUE)
+    {
+        conf.baud = DEFAULT_BAUD_VALUE;
+    }
+    else if(conf.baud <= 0)
+    {
+        applog(LOG_ERR, "$$$$Config argument Baudrate:%d err\n", conf.baud);
+        return -1;
+    }
+
+    if(Conf.DataCount > MAX_WORK || Conf.DataCount <= 0)
+    {
+        applog(LOG_ERR, "$$$$Config argument DataCount:%d err\n", Conf.DataCount);
+    }
+    else
+        conf.dataCount = Conf.DataCount;
+
+    if(Conf.PassCount1 > conf.dataCount || Conf.PassCount1 < 0)
+    {
+        applog(LOG_ERR, "$$$$Config argument DataCount:%d err\n", Conf.DataCount);
+    }
+    else
+        conf.passCount1 = Conf.PassCount1;
+
+    if(Conf.PassCount2 > conf.dataCount || Conf.PassCount2 < 0)
+    {
+        applog(LOG_ERR, "$$$$Config argument DataCount:%d err\n", Conf.DataCount);
+    }
+    else
+        conf.passCount2 = Conf.PassCount2;
+
+    if(Conf.PassCount3 > conf.dataCount || Conf.PassCount3 < 0)
+    {
+        applog(LOG_ERR, "$$$$Config argument DataCount:%d err\n", Conf.DataCount);
+    }
+    else
+        conf.passCount3 = Conf.PassCount3;
+
+    return 0;
+}
+
+static void print_config()
+{
+    const struct configuration *m_conf = &Conf;
+    printf("\n\nRead Config.ini\n");
+    printf("DataCount:%d\n", m_conf->DataCount);
+    printf("PassCount1:%d\n", m_conf->PassCount1);
+    printf("PassCount2:%d\n", m_conf->PassCount2);
+    printf("PassCount3:%d\n", m_conf->PassCount3);
+    printf("Freq:%d\n", m_conf->Freq);
+    printf("Timeout:%d\n", m_conf->Timeout);
+    printf("OpenCoreGap:%d\n", m_conf->OpenCoreGap);
+    printf("CheckTemp:%d\n", m_conf->checktemp);
+    printf("CoreNum:%d\n", m_conf->CoreNum);
+    printf("freq_e:%d\n", m_conf->freq_e);
+    printf("AsicNum:%d\n", m_conf->AsicNum);
+    printf("TestMode:%d\n", m_conf->TestMode);
+    printf("CheckChain:%d\n", m_conf->CheckChain);
+    printf("CommandMode:%d\n", m_conf->CommandMode);
+    printf("AsicType:%d\n", m_conf->AsicType);
+    printf("ValidNonce1:%d\n", m_conf->ValidNonce1);
+    printf("ValidNonce2:%d\n", m_conf->ValidNonce2);
+    printf("ValidNonce3:%d\n", m_conf->ValidNonce3);
+    printf("Pic:%ud\n", m_conf->Pic);
+    printf("IICPic:%ud\n", m_conf->IICPic);
+    printf("dac = %ud\n", m_conf->dac);
+    printf("Voltage1:%ud\n", m_conf->Voltage1);
+    printf("Voltage2:%ud\n", m_conf->Voltage2);
+    printf("Voltage3:%ud\n", m_conf->Voltage3);
+    printf("OpenCoreNum1 = %ud = 0x%x\n", m_conf->OpenCoreNum1, m_conf->OpenCoreNum1);
+    printf("OpenCoreNum2 = %ud = 0x%x\n", m_conf->OpenCoreNum2, m_conf->OpenCoreNum2);
+    printf("OpenCoreNum3 = %ud = 0x%x\n", m_conf->OpenCoreNum3, m_conf->OpenCoreNum3);
+    printf("OpenCoreNum4 = %ud = 0x%x\n", m_conf->OpenCoreNum4, m_conf->OpenCoreNum4);
+    printf("GetTempFrom:%d\n", m_conf->GetTempFrom);
+    printf("TempSel:%d\n", m_conf->TempSel);
+    printf("TempSensor1:%d\n", m_conf->TempSensor1);
+    printf("TempSensor2:%d\n", m_conf->TempSensor2);
+    printf("TempSensor3:%d\n", m_conf->TempSensor3);
+    printf("TempSensor4:%d\n", m_conf->TempSensor4);
+    printf("DefaultTempOffset:%d\n", m_conf->DefaultTempOffset);
+    printf("StartSensor:%d\n", m_conf->StartSensor);
+    printf("StartTemp:%d\n", m_conf->StartTemp);
+    printf("year:%04d\n", m_conf->year);
+    printf("month:%02d\n", m_conf->month);
+    printf("date:%02d\n", m_conf->date);
+    printf("hour:%02d\n", m_conf->hour);
+    printf("minute:%02d\n", m_conf->minute);
+    printf("second:%02d\n", m_conf->second);
+
+    printf("\n\n");
+}
+
+static void print_CONFIG(void)
+{
+    const struct _CONFIG *m_conf = &conf;
+    printf("\n\nparameter processed after Reading Config.ini\n");
+    printf("DataCount:%d\n", m_conf->dataCount);
+    printf("PassCount1:%d\n", m_conf->passCount1);
+    printf("PassCount2:%d\n", m_conf->passCount2);
+    printf("PassCount3:%d\n", m_conf->passCount3);
+    printf("Freq:%d\n", m_conf->freq);
+    printf("Timeout:%d\n", m_conf->timeout);
+    printf("OpenCoreGap:%d\n", m_conf->OpenCoreGap);
+    printf("CheckTemp:%d\n", m_conf->checktemp);
+    printf("CoreNum:%d\n", m_conf->core);
+    printf("AsicNum:%d\n", m_conf->asicNum);
+    printf("TestMode:%d\n", m_conf->testMode);
+    printf("CommandMode:%d\n", m_conf->CommandMode);
+    printf("AsicType:%d\n", m_conf->AsicType);
+    printf("ValidNonce1:%d\n", m_conf->ValidNonce1);
+    printf("ValidNonce2:%d\n", m_conf->ValidNonce2);
+    printf("ValidNonce3:%d\n", m_conf->ValidNonce3);
+    printf("Pic:%ud\n", m_conf->Pic);
+    printf("IICPic:%ud\n", m_conf->IICPic);
+    printf("dac:%ud\n", m_conf->dac);
+    printf("Voltage1:%ud\n", m_conf->Voltage1);
+    printf("Voltage2:%ud\n", m_conf->Voltage2);
+    printf("Voltage3:%ud\n", m_conf->Voltage3);
+    printf("OpenCoreNum1 = %ud = 0x%x\n", m_conf->OpenCoreNum1, m_conf->OpenCoreNum1);
+    printf("OpenCoreNum2 = %ud = 0x%x\n", m_conf->OpenCoreNum2, m_conf->OpenCoreNum2);
+    printf("OpenCoreNum3 = %ud = 0x%x\n", m_conf->OpenCoreNum3, m_conf->OpenCoreNum3);
+    printf("OpenCoreNum4 = %ud = 0x%x\n", m_conf->OpenCoreNum4, m_conf->OpenCoreNum4);
+    printf("GetTempFrom:%d\n", m_conf->GetTempFrom);
+    printf("TempSel:%d\n", m_conf->TempSel);
+    printf("TempSensor1:%d\n", m_conf->TempSensor1);
+    printf("TempSensor2:%d\n", m_conf->TempSensor2);
+    printf("TempSensor3:%d\n", m_conf->TempSensor3);
+    printf("TempSensor4:%d\n", m_conf->TempSensor4);
+    printf("DefaultTempOffset:%d\n", m_conf->DefaultTempOffset);
+    printf("StartSensor:%d\n", m_conf->StartSensor);
+    printf("StartTemp:%d\n", m_conf->StartTemp);
+    printf("year:%04d\n", m_conf->year);
+    printf("month:%02d\n", m_conf->month);
+    printf("date:%02d\n", m_conf->date);
+    printf("hour:%02d\n", m_conf->hour);
+    printf("minute:%02d\n", m_conf->minute);
+    printf("second:%02d\n", m_conf->second);
+    printf("\n\n");
+}
+
+static int get_works()
+{
+    char strFilePath[64] = {0};
+    int i, j, record, loop=0;
+    unsigned int OpenCoreNum1 = conf.OpenCoreNum1;
+    unsigned int OpenCoreNum2 = conf.OpenCoreNum2;
+    unsigned int OpenCoreNum3 = conf.OpenCoreNum3;
+    unsigned int OpenCoreNum4 = conf.OpenCoreNum4;
+    //getcwd(Path, 128);
+    //applog(LOG_DEBUG, "Path:%s\n", Path);
+
+    //printf("%s: loop = %d\n", __FUNCTION__, loop);
+
+    if(CHAIN_ASIC_NUM == 1)
+    {
+        for(j=0; j < 32; j++)
+        {
+            if(OpenCoreNum1 & 0x00000001)
+            {
+                loop++;
+            }
+            OpenCoreNum1 = OpenCoreNum1 >> 1;
+
+            if(OpenCoreNum2 & 0x00000001)
+            {
+                loop++;
+            }
+            OpenCoreNum2 = OpenCoreNum2 >> 1;
+
+            if(OpenCoreNum3 & 0x00000001)
+            {
+                loop++;
+            }
+            OpenCoreNum3 = OpenCoreNum3 >> 1;
+
+            if(OpenCoreNum4 & 0x00000001)
+            {
+                loop++;
+            }
+            OpenCoreNum4 = OpenCoreNum4 >> 1;
+        }
+
+        printf("%s: loop = %d\n", __FUNCTION__, loop);
+    }
+    else
+    {
+        loop = conf.asicNum;
+    }
+
+    j=0;
+    OpenCoreNum1 = conf.OpenCoreNum1;
+    OpenCoreNum2 = conf.OpenCoreNum2;
+    OpenCoreNum3 = conf.OpenCoreNum3;
+    OpenCoreNum4 = conf.OpenCoreNum4;
+
+    for(i = 0; i < loop; i++)
+    {
+        if(CHAIN_ASIC_NUM == 1)
+        {
+            for(; j < 128; j++)
+            {
+                if(j < 32)
+                {
+                    if(OpenCoreNum1 & 0x00000001)
+                    {
+                        sprintf(strFilePath, "%s%02i.bin", cgpu.workdataPathPrefix, j+1);
+                        printf("dir:%s\n", strFilePath);
+                        OpenCoreNum1 = OpenCoreNum1 >> 1;
+                        j++;
+                        break;
+                    }
+                    else
+                    {
+                        OpenCoreNum1 = OpenCoreNum1 >> 1;
+                    }
+                }
+                else if((j >= 32) && (j < 64))
+                {
+                    if(OpenCoreNum2 & 0x00000001)
+                    {
+                        sprintf(strFilePath, "%s%02i.bin", cgpu.workdataPathPrefix, j+1);
+                        printf("dir:%s\n", strFilePath);
+                        OpenCoreNum2 = OpenCoreNum2 >> 1;
+                        j++;
+                        break;
+                    }
+                    else
+                    {
+                        OpenCoreNum2 = OpenCoreNum2 >> 1;
+                    }
+                }
+                else if((j >= 64) && (j < 96))
+                {
+                    if(OpenCoreNum3 & 0x00000001)
+                    {
+                        sprintf(strFilePath, "%s%02i.bin", cgpu.workdataPathPrefix, j+1);
+                        printf("dir:%s\n", strFilePath);
+                        OpenCoreNum3 = OpenCoreNum3 >> 1;
+                        j++;
+                        break;
+                    }
+                    else
+                    {
+                        OpenCoreNum3 = OpenCoreNum3 >> 1;
+                    }
+                }
+                else
+                {
+                    if(OpenCoreNum4 & 0x00000001)
+                    {
+                        sprintf(strFilePath, "%s%02i.bin", cgpu.workdataPathPrefix, j+1);
+                        printf("dir:%s\n", strFilePath);
+                        OpenCoreNum4 = OpenCoreNum4 >> 1;
+                        j++;
+                        break;
+                    }
+                    else
+                    {
+                        OpenCoreNum4 = OpenCoreNum4 >> 1;
+                    }
+                }
+            }
+        }
+        else
+        {
+            sprintf(strFilePath, "%s%02i.bin", cgpu.workdataPathPrefix, i+1);
+            //applog(LOG_DEBUG, "dir:%s\n", strFilePath);
+        }
+
+        cgpu.fps[i] = fopen(strFilePath, "rb");
+        if(NULL == cgpu.fps[i])
+        {
+            applog(LOG_ERR, "Open test file %s error\n", strFilePath);
+            return -1;
+        }
+        cgpu.subid[i] = load_testpatten_work(i, MAX_WORK);
+        //applog(LOG_DEBUG, "asic[%d] get work %d\n", i, cgpu.subid[i]);
+        fclose(cgpu.fps[i]);
+    }
+
+    cgpu.min_work_subid = cgpu.subid[0];
+    record = 0;
+    for(i = 0; i < loop; i++)
+    {
+        if(cgpu.min_work_subid > cgpu.subid[i])
+        {
+            cgpu.min_work_subid = cgpu.subid[i];
+            record = i;
+        }
+    }
+    applog(LOG_DEBUG, "min work minertest[%d]:%d\n\n\n", record, cgpu.min_work_subid);
+    if(conf.dataCount > cgpu.min_work_subid)
+    {
+        applog(LOG_ERR, "$$$$dataCount=%d, but min work subid=%d\n",
+               conf.dataCount, cgpu.min_work_subid);
+        return -1;
+    }
+    return 0;
+}
+
+
+static int configMiner()
+{
+    int ret;
+
+    read_config();
+    print_config();
+    ret = process_config();
+    if(ret < 0) return -EFAULT;
+
+    print_CONFIG();
+    ret = get_works();
+    if(ret < 0) return -EFAULT;
+    return 0;
+}
+
+static int calculate_asic_number(unsigned int actual_asic_number)
+{
+    int i = 0;
+    if(actual_asic_number == 1)
+    {
+        i = 1;
+    }
+    else if(actual_asic_number == 2)
+    {
+        i = 2;
+    }
+    else if((actual_asic_number > 2) && (actual_asic_number <= 4))
+    {
+        i = 4;
+    }
+    else if((actual_asic_number > 4) && (actual_asic_number <= 8))
+    {
+        i = 8;
+    }
+    else if((actual_asic_number > 8) && (actual_asic_number <= 16))
+    {
+        i = 16;
+    }
+    else if((actual_asic_number > 16) && (actual_asic_number <= 32))
+    {
+        i = 32;
+    }
+    else if((actual_asic_number > 32) && (actual_asic_number <= 64))
+    {
+        i = 64;
+    }
+    else if((actual_asic_number > 64) && (actual_asic_number <= 128))
+    {
+        i = 128;
+    }
+    else
+    {
+        applog(LOG_DEBUG,"actual_asic_number = %d, but it is error\n", actual_asic_number);
+        return -1;
+    }
+    return i;
+}
+
+static int calculate_core_number(unsigned int actual_core_number)
+{
+    int i = 0;
+    if(actual_core_number == 1)
+    {
+        i = 1;
+    }
+    else if(actual_core_number == 2)
+    {
+        i = 2;
+    }
+    else if((actual_core_number > 2) && (actual_core_number <= 4))
+    {
+        i = 4;
+    }
+    else if((actual_core_number > 4) && (actual_core_number <= 8))
+    {
+        i = 8;
+    }
+    else if((actual_core_number > 8) && (actual_core_number <= 16))
+    {
+        i = 16;
+    }
+    else if((actual_core_number > 16) && (actual_core_number <= 32))
+    {
+        i = 32;
+    }
+    else if((actual_core_number > 32) && (actual_core_number <= 64))
+    {
+        i = 64;
+    }
+    else if((actual_core_number > 64) && (actual_core_number <= 128))
+    {
+        i = 128;
+    }
+    else
+    {
+        applog(LOG_DEBUG,"actual_core_number = %d, but it is error\n", actual_core_number);
+        return -1;
+    }
+    return i;
+}
+
+static int get_result(int chainIndex, int passCount, int validnonce)
+{
+    char logstr[1024];
+    int ret = 3;
+    int i, j=0, loop=0, m, n;
+    unsigned int OpenCoreNum1 = conf.OpenCoreNum1;
+    unsigned int OpenCoreNum2 = conf.OpenCoreNum2;
+    unsigned int OpenCoreNum3 = conf.OpenCoreNum3;
+    unsigned int OpenCoreNum4 = conf.OpenCoreNum4;
+
+    printf("\n------------------------------------------------------------------------------------------------------\n");
+    if(conf.CommandMode)
+    {
+        printf("Command mode is FIL\n");
+    }
+    else
+    {
+        printf("Command mode is VIL\n");
+    }
+
+    if(cgpu.real_asic_num == 1)
+    {
+        printf("Open core number : Conf.OpenCoreNum1 = %ud = 0x%x\n", Conf.OpenCoreNum1, Conf.OpenCoreNum1);
+        printf("Open core number : Conf.OpenCoreNum2 = %ud = 0x%x\n", Conf.OpenCoreNum2, Conf.OpenCoreNum2);
+        printf("Open core number : Conf.OpenCoreNum3 = %ud = 0x%x\n", Conf.OpenCoreNum3, Conf.OpenCoreNum3);
+        printf("Open core number : Conf.OpenCoreNum4 = %ud = 0x%x\n", Conf.OpenCoreNum4, Conf.OpenCoreNum4);
+        loop = Conf.CoreNum;
+    }
+    else
+    {
+        loop = cgpu.real_asic_num;
+    }
+    sprintf(logstr,"require nonce number:%d\n", passCount);
+    writeLogFile(logstr);
+
+    sprintf(logstr,"require validnonce number:%d\n", validnonce);
+    writeLogFile(logstr);
+
+    for(i = 0; i < loop; i++)
+    {
+#ifdef LOG_CHIPS_CORE_DETAIL
+        if(cgpu.real_asic_num == 1)
+        {
+            sprintf(logstr,"core[%02d]=%02d\t", i, asic_nonce_num[chainIndex][i]);
+            writeLogFile(logstr);
+        }
+        else
+        {
+            sprintf(logstr,"asic[%02d]=%02d\t", i, asic_nonce_num[chainIndex][i]);
+            writeLogFile(logstr);
+        }
+
+        if(i % 8 == 7)
+        {
+            sprintf(logstr,"\n");
+            writeLogFile(logstr);
+        }
+#endif
+        if(cgpu.real_asic_num == 1)
+        {
+            for(; j < 128; j++)
+            {
+                if(j < 32)
+                {
+                    if(OpenCoreNum1 & 0x00000001)
+                    {
+                        if(asic_nonce_num[chainIndex][j] < passCount)
+                        {
+                            ret = (~0x00000001) & ret;
+                        }
+                        OpenCoreNum1 = OpenCoreNum1 >> 1;
+                    }
+                    else
+                    {
+                        OpenCoreNum1 = OpenCoreNum1 >> 1;
+                    }
+                }
+                else if((j >= 32) && (j < 64))
+                {
+                    if(OpenCoreNum2 & 0x00000001)
+                    {
+                        if(asic_nonce_num[chainIndex][j] < passCount)
+                        {
+                            ret = (~0x00000001) & ret;
+                        }
+                        OpenCoreNum2 = OpenCoreNum2 >> 1;
+                    }
+                    else
+                    {
+                        OpenCoreNum2 = OpenCoreNum2 >> 1;
+                    }
+                }
+                else if((j >= 64) && (j < 96))
+                {
+                    if(OpenCoreNum3 & 0x00000001)
+                    {
+                        if(asic_nonce_num[chainIndex][j] < passCount)
+                        {
+                            ret = (~0x00000001) & ret;
+                        }
+                        OpenCoreNum3 = OpenCoreNum3 >> 1;
+                    }
+                    else
+                    {
+                        OpenCoreNum3 = OpenCoreNum3 >> 1;
+                    }
+                }
+                else
+                {
+                    if(OpenCoreNum4 & 0x00000001)
+                    {
+                        if(asic_nonce_num[chainIndex][j] < passCount)
+                        {
+                            ret = (~0x00000001) & ret;
+                        }
+                        OpenCoreNum4 = OpenCoreNum4 >> 1;
+                    }
+                    else
+                    {
+                        OpenCoreNum4 = OpenCoreNum4 >> 1;
+                    }
+                }
+            }
+        }
+        else
+        {
+            if(asic_nonce_num[chainIndex][i] < passCount)
+            {
+                ret = (~0x00000001) & ret;
+            }
+        }
+    }
+
+    if((Conf.StartSensor > 0) && (cgpu.real_asic_num != 1))
+    {
+        n = passCount/Conf.CoreNum;
+#ifdef LOG_CHIPS_CORE_DETAIL
+        sprintf(logstr,"\n\n\nBelow ASIC's core didn't receive all the nonce, they should receive %d nonce each!\n\n", n);
+        writeLogFile(logstr);
+#endif
+        for(i = 0; i < loop; i++)
+        {
+            int opened_core_num=0;
+            for(m=0; m<Conf.CoreNum; m++)
+            {
+                if(asic_core_nonce_num[chainIndex][i][m]>0) // we only check core is open
+                    opened_core_num++;
+            }
+
+            if(opened_core_num >= Conf.CoreNum-chain_badcore_num[chainIndex][i])
+                last_result_opencore[chainIndex][i]=1;
+            else last_result_opencore[chainIndex][i]=0;
+
+            if(asic_nonce_num[chainIndex][i] < passCount-chain_badcore_num[chainIndex][i]*n)
+                last_result[chainIndex][i]=0;
+            else last_result[chainIndex][i]=1;
+
+#ifdef LOG_CHIPS_CORE_DETAIL
+            if(asic_nonce_num[chainIndex][i] < passCount)
+            {
+                sprintf(logstr,"asic[%02d]=%02d\n", i, asic_nonce_num[chainIndex][i]);
+                writeLogFile(logstr);
+
+                for(m=0; m<Conf.CoreNum; m++)
+                {
+                    if(asic_core_nonce_num[chainIndex][i][m] != n)
+                    {
+                        sprintf(logstr,"core[%03d]=%d\t", m, asic_core_nonce_num[chainIndex][i][m]);
+                        writeLogFile(logstr);
+                    }
+                }
+                sprintf(logstr,"\n\n");
+                writeLogFile(logstr);
+            }
+#endif
+        }
+    }
+
+    sprintf(logstr,"\n\n");
+    writeLogFile(logstr);
+
+    for(i = 0; i < loop; i++)
+    {
+        sprintf(logstr,"freq[%02d]=%d\t", i, get_freqvalue_by_index(getChainAsicFreqIndex(chainIndex,i)));
+        writeLogFile(logstr);
+
+        if(i % 8 == 7)
+        {
+            sprintf(logstr,"\n");
+            writeLogFile(logstr);
+        }
+    }
+
+    sprintf(logstr,"\n\n");
+    writeLogFile(logstr);
+
+    if(valid_nonce_num[chainIndex] < validnonce)
+    {
+        ret = (~0x00000001) & ret;
+    }
+
+    sprintf(logstr,"total valid nonce number:%d\n", valid_nonce_num[chainIndex]);
+    writeLogFile(logstr);
+    sprintf(logstr,"total send work number:%d\n", send_work_num[chainIndex]);
+    writeLogFile(logstr);
+    sprintf(logstr,"require valid nonce number:%d\n", validnonce);
+    writeLogFile(logstr);
+
+    sprintf(logstr,"repeated_nonce_num:%d\n", repeated_nonce_num[chainIndex]);
+    writeLogFile(logstr);
+    sprintf(logstr,"err_nonce_num:%d\n", err_nonce_num[chainIndex]);
+    writeLogFile(logstr);
+    sprintf(logstr,"last_nonce_num:%d\n", last_nonce_num[chainIndex]);
+    writeLogFile(logstr);
+    return ret;
+}
+
+
+
+////////////////////////////////////TEST PATTEN MAIN.C /////////////////////////////////
+static int reset_work_data(void)
+{
+    int i, j;
+    for(i =0 ; i < conf.asicNum; i++)
+    {
+        for(j = 0; j < conf.dataCount; j++)
+        {
+            cgpu.results[i][j] = 0;
+        }
+        cgpu.result_array[i] = 0;
+    }
+    cgpu.index = 0;
+    cgpu.valid_nonce = 0;
+    cgpu.err_nonce = 0;
+    cgpu.repeated_nonce = 0;
+    return 0;
+}
+
+static int cgpu_init(void)
+{
+    int ret = 0;
+    memset(&cgpu, 0, sizeof(struct testpatten_cgpu_info));
+    return 0;
+}
+
+static int send_func_all()
+{
+    int which_asic[BITMAIN_MAX_CHAIN_NUM];
+    int i,j;
+    unsigned int work_fifo_ready = 0;
+    int index[BITMAIN_MAX_CHAIN_NUM];
+    struct testpatten_work * works, *work;
+    unsigned char data_fil[TW_WRITE_COMMAND_LEN] = {0xff};
+    unsigned char data_vil[TW_WRITE_COMMAND_LEN_VIL] = {0xff};
+    struct vil_work_1387 work_vil_1387;
+    unsigned int buf[TW_WRITE_COMMAND_LEN/sizeof(unsigned int)]= {0};
+    unsigned int buf_vil[TW_WRITE_COMMAND_LEN_VIL/sizeof(unsigned int)]= {0};
+    int chainIndex;
+    char logstr[1024];
+    bool isSendOver=false;
+    int wait_counter=0;
+    bool sendStartFlag[BITMAIN_MAX_CHAIN_NUM];
+
+    for(i=0; i<BITMAIN_MAX_CHAIN_NUM; i++)
+    {
+        index[i]=0;
+        which_asic[i]=0;
+        sendStartFlag[i]=StartSendFlag[i];
+    }
+
+    while(!isSendOver)
+    {
+        // send work
+        for(chainIndex=0; chainIndex<BITMAIN_MAX_CHAIN_NUM; chainIndex++)
+        {
+            if(cgpu.chain_exist[chainIndex] == 0 || (!sendStartFlag[chainIndex]))
+                continue;
+
+            while(which_asic[chainIndex] < CHAIN_ASIC_NUM)
+            {
+                work_fifo_ready = get_buffer_space();
+                if(work_fifo_ready & (0x1 << chainIndex))   // work fifo is not full, we can send work
+                {
+                    wait_counter=0; // clear wait fifo counter
+
+                    if(cgpu.CommandMode)    // fil mode
+                    {
+                        memset(buf, 0x0, TW_WRITE_COMMAND_LEN/sizeof(unsigned int));
+
+                        // get work for sending to asic
+                        works = cgpu.works[which_asic[chainIndex]]; // which ASIC
+                        work = works + index[chainIndex];      // which test data for the ASIC
+
+                        // parse work data
+                        memset(data_fil, 0x0, TW_WRITE_COMMAND_LEN);
+                        data_fil[0] = NORMAL_BLOCK_MARKER;
+                        data_fil[1] = chainIndex | 0x80; //set chain id and enable it
+                        for(i=0; i<MIDSTATE_LEN; i++)
+                        {
+                            data_fil[i+4] = work->midstate[i];
+                        }
+                        for(i=0; i<DATA2_LEN; i++)
+                        {
+                            data_fil[i+40] = work->data[i];
+                        }
+
+                        // send work
+                        //printf("\n");
+                        for(j=0; j<TW_WRITE_COMMAND_LEN/sizeof(unsigned int); j++)
+                        {
+                            buf[j] = (data_fil[4*j + 0] << 24) | (data_fil[4*j + 1] << 16) | (data_fil[4*j + 2] << 8) | data_fil[4*j + 3];
+                            if(j==9)
+                            {
+                                buf[j] = index[chainIndex];
+                            }
+                            //applog(LOG_DEBUG,"%s: buf[%d] = 0x%08x\n", __FUNCTION__, j, buf[j]);
+                        }
+
+#ifndef DEBUG_XILINX_NONCE_NOTENOUGH
+                        pthread_mutex_lock(&opencore_readtemp_mutex);
+#endif
+                        set_TW_write_command(buf);
+
+#ifndef DEBUG_XILINX_NONCE_NOTENOUGH
+                        pthread_mutex_unlock(&opencore_readtemp_mutex);
+#endif
+                        which_asic[chainIndex]++;
+                    }
+                    else    // vil mode
+                    {
+                        if(ASIC_TYPE == 1387)
+                        {
+                            //printf("\n--- send work\n");
+                            memset(buf_vil, 0x0, TW_WRITE_COMMAND_LEN_VIL/sizeof(unsigned int));
+
+                            works = cgpu.works[which_asic[chainIndex]]; // which ASIC
+                            work = works + index[chainIndex];      // which test data for the ASIC
+
+                            // parse work data
+                            memset(&work_vil_1387, 0, sizeof(struct vil_work_1387));
+                            work_vil_1387.work_type = NORMAL_BLOCK_MARKER;
+                            work_vil_1387.chain_id = 0x80 | chainIndex;
+                            work_vil_1387.reserved1[0]= 0;
+                            work_vil_1387.reserved1[1]= 0;
+                            work_vil_1387.work_count = index[chainIndex];
+                            for(i=0; i<DATA2_LEN; i++)
+                            {
+                                work_vil_1387.data[i] = work->data[i];
+                            }
+                            for(i=0; i<MIDSTATE_LEN; i++)
+                            {
+                                work_vil_1387.midstate[i] = work->midstate[i];
+                            }
+
+                            // send work
+                            buf_vil[0] = (work_vil_1387.work_type << 24) | (work_vil_1387.chain_id << 16) | (work_vil_1387.reserved1[0] << 8) | work_vil_1387.reserved1[1];
+                            buf_vil[1] = work_vil_1387.work_count;
+                            for(j=2; j<DATA2_LEN/sizeof(int)+2; j++)
+                            {
+                                buf_vil[j] = (work_vil_1387.data[4*(j-2) + 0] << 24) | (work_vil_1387.data[4*(j-2) + 1] << 16) | (work_vil_1387.data[4*(j-2) + 2] << 8) | work_vil_1387.data[4*(j-2) + 3];
+                            }
+                            for(j=5; j<MIDSTATE_LEN/sizeof(unsigned int)+5; j++)
+                            {
+                                buf_vil[j] = (work_vil_1387.midstate[4*(j-5) + 0] << 24) | (work_vil_1387.midstate[4*(j-5) + 1] << 16) | (work_vil_1387.midstate[4*(j-5) + 2] << 8) | work_vil_1387.midstate[4*(j-5) + 3];;
+                            }
+
+#ifndef DEBUG_XILINX_NONCE_NOTENOUGH
+                            pthread_mutex_lock(&opencore_readtemp_mutex);
+#endif
+                            set_TW_write_command_vil(buf_vil);
+
+#ifndef DEBUG_XILINX_NONCE_NOTENOUGH
+                            pthread_mutex_unlock(&opencore_readtemp_mutex);
+#endif
+                            which_asic[chainIndex]++;
+
+                        }
+                        else
+                        {
+                            //printf("\n--- send work\n");
+                            memset(buf_vil, 0x0, TW_WRITE_COMMAND_LEN_VIL/sizeof(unsigned int));
+                            // get work for sending to asic
+                            //work_fil = (struct testpatten_work *)((void *)cgpu.works[which_asic] + index*sizeof(struct testpatten_work));
+                            works = cgpu.works[which_asic[chainIndex]]; // which ASIC
+                            work = works + index[chainIndex];      // which test data for the ASIC
+
+                            // parse work data
+                            memset(data_vil, 0x00, TW_WRITE_COMMAND_LEN_VIL);
+                            data_vil[0] = NORMAL_BLOCK_MARKER;
+                            data_vil[1] = chainIndex | 0x80; //set chain id and enable it
+                            data_vil[4] = 0x01 << 5;                // type
+                            data_vil[5] = sizeof(struct vil_work);  // length
+                            data_vil[6] = index[chainIndex];               // wc_base / work_id
+                            data_vil[7] = 0x01;                     // mid_num
+
+                            for(i=0; i<MIDSTATE_LEN; i++)
+                            {
+                                data_vil[i+8] = work->midstate[i];
+                            }
+                            for(i=0; i<DATA2_LEN; i++)
+                            {
+                                data_vil[i+40] = work->data[i];
+                            }
+
+                            // send work
+                            for(j=0; j<TW_WRITE_COMMAND_LEN_VIL/sizeof(unsigned int); j++)
+                            {
+                                buf_vil[j] = (data_vil[4*j + 0] << 24) | (data_vil[4*j + 1] << 16) | (data_vil[4*j + 2] << 8) | data_vil[4*j + 3];
+                                //printf("%s: buf_vil[%d] = 0x%08x\n", __FUNCTION__, j, buf_vil[j]);
+                            }
+
+#ifndef DEBUG_XILINX_NONCE_NOTENOUGH
+                            pthread_mutex_lock(&opencore_readtemp_mutex);
+#endif
+                            set_TW_write_command_vil(buf_vil);
+
+#ifndef DEBUG_XILINX_NONCE_NOTENOUGH
+                            pthread_mutex_unlock(&opencore_readtemp_mutex);
+#endif
+                            which_asic[chainIndex]++;
+                        }
+                    }
+                    send_work_num[chainIndex]++;
+                    //printf("%s: send_work_num = %d\n", __FUNCTION__, send_work_num);
+                }
+                else    //work fifo is full, wait for 1ms
+                {
+                    wait_counter++;
+                    break;
+                }
+            }
+
+            if(which_asic[chainIndex] >= CHAIN_ASIC_NUM)
+            {
+                which_asic[chainIndex]=0;   // then send from chip[0] ....
+                index[chainIndex]++;    // switch to next work
+                if(index[chainIndex] >= chain_DataCount[chainIndex])
+                    sendStartFlag[chainIndex]=false;
+            }
+
+            if(wait_counter>2000)
+            {
+                // timeout on wait for fifo ready
+                sprintf(logstr,"Fatal Error: send work timeout\n");
+                writeLogFile(logstr);
+                break;
+            }
+        }
+        usleep(5000);
+
+        isSendOver=true;
+        for(i=0; i<BITMAIN_MAX_CHAIN_NUM; i++)
+        {
+            if(cgpu.chain_exist[i] == 0 || (!StartSendFlag[i]))
+                continue;
+
+            if(index[i] < chain_DataCount[i])
+            {
+                isSendOver=false;
+            }
+        }
+    }
+
+    for(i=0; i<BITMAIN_MAX_CHAIN_NUM; i++)
+    {
+        if(cgpu.chain_exist[i] == 0 || (!StartSendFlag[i]))
+            continue;
+
+        StartSendFlag[i]=false; // when send over , must set this flag to false!!!
+        sprintf(logstr,"get send work num :%d on Chain[%d]\n", send_work_num[i],i);
+        writeLogFile(logstr);
+
+        sendExit[i]=true;
+    }
+
+    return 0;
+}
+
+static uint32_t last_nonce[BITMAIN_MAX_CHAIN_NUM], llast_nonce[BITMAIN_MAX_CHAIN_NUM];
+static unsigned int work_id[BITMAIN_MAX_CHAIN_NUM];
+static unsigned int m_nonce[BITMAIN_MAX_CHAIN_NUM];
+static void *receive_func(void *arg)
+{
+    unsigned int j=0, n=0, nonce_number = 0, read_loop=0;
+    unsigned int buf[2] = {0,0};
+
+    uint8_t which_asic_nonce = 0;
+    uint8_t which_core_nonce = 0;
+    uint8_t whose_nonce = 0, nonce_index=0;
+    unsigned int OpenCoreNum1 = conf.OpenCoreNum1;
+    unsigned int OpenCoreNum2 = conf.OpenCoreNum2;
+    unsigned int OpenCoreNum3 = conf.OpenCoreNum3;
+    unsigned int OpenCoreNum4 = conf.OpenCoreNum4;
+    char logstr[1024];
+    int chainIndex;
+
+    memset(repeated_nonce_id, 0xff, sizeof(repeated_nonce_id));
+    memset(last_nonce,0x00,sizeof(last_nonce));
+    memset(llast_nonce,0x00,sizeof(llast_nonce));
+    memset(work_id,0x00,sizeof(work_id));
+    memset(m_nonce,0x00,sizeof(m_nonce));
+
+    while(!ExitFlag)
+    {
+        if(!start_receive)
+        {
+            j=0;
+            n=0;
+            nonce_number = 0;
+            read_loop=0;
+            buf[0]=0;
+            buf[1]=0;
+
+            which_asic_nonce = 0;
+            which_core_nonce = 0;
+            whose_nonce = 0;
+            nonce_index=0;
+            OpenCoreNum1 = conf.OpenCoreNum1;
+            OpenCoreNum2 = conf.OpenCoreNum2;
+            OpenCoreNum3 = conf.OpenCoreNum3;
+            OpenCoreNum4 = conf.OpenCoreNum4;
+
+            memset(repeated_nonce_id, 0xff, sizeof(repeated_nonce_id));
+            memset(last_nonce,0x00,sizeof(last_nonce));
+            memset(llast_nonce,0x00,sizeof(llast_nonce));
+            memset(work_id,0x00,sizeof(work_id));
+            memset(m_nonce,0x00,sizeof(m_nonce));
+
+            usleep(100000);
+            continue;
+        }
+
+        read_loop = 0;
+
+        nonce_number = get_nonce_number_in_fifo() & MAX_NONCE_NUMBER_IN_FIFO;
+        //applog(LOG_DEBUG,"%s: --- nonce_number = %d\n", __FUNCTION__, nonce_number);
+        if(nonce_number>0)
+        {
+            read_loop = nonce_number;
+            //applog(LOG_DEBUG,"%s: read_loop = %d\n", __FUNCTION__, read_loop);
+
+            for(j=0; j<read_loop; j++)
+            {
+                get_return_nonce(buf);
+                //printf("%s: buf[0] = 0x%08x\n", __FUNCTION__, buf[0]);
+                //printf("%s: buf[1] = 0x%08x\n", __FUNCTION__, buf[1]);
+                if(buf[0] & WORK_ID_OR_CRC) //nonce
+                {
+                    if(gBegin_get_nonce)
+                    {
+                        if(buf[0] & NONCE_INDICATOR)
+                        {
+                            chainIndex=CHAIN_NUMBER(buf[0]);
+                            if(chainIndex<0 || chainIndex>=BITMAIN_MAX_CHAIN_NUM)
+                            {
+                                sprintf(logstr,"Error chain index of nonce!!!\n");
+                                writeLogFile(logstr);
+                                continue;
+                            }
+
+                            if(cgpu.CommandMode)    // fil mode
+                            {
+                                work_id[chainIndex] = (buf[0] >> 16) & 0x00007fff;
+                            }
+                            else    // vil mode
+                            {
+                                if(ASIC_TYPE == 1387)
+                                {
+                                    work_id[chainIndex] = (buf[0] >> 16) & 0x00007fff;
+                                }
+                                else
+                                {
+                                    work_id[chainIndex] = (buf[0] >> 24) & 0x0000007f;
+                                }
+                            }
+
+                            if((buf[1] == last_nonce[chainIndex]) || (buf[1] == llast_nonce[chainIndex]))
+                            {
+                                last_nonce_num[chainIndex]++;
+                                continue;
+                            }
+
+                            if(cgpu.real_asic_num == 1)
+                            {
+                                if(conf.core <= 64)
+                                {
+                                    which_core_nonce = (buf[1] & 0x0000003f);
+                                    whose_nonce = which_core_nonce;
+                                }
+                                else if((conf.core <= 128) && (conf.core > 64))
+                                {
+                                    which_core_nonce = (buf[1] & 0x0000007f);
+                                    if(which_core_nonce <= 56)
+                                    {
+                                        whose_nonce = which_core_nonce;
+                                    }
+                                    else if((which_core_nonce >= 64) && (which_core_nonce < 128))
+                                    {
+                                        whose_nonce = which_core_nonce - 7;
+                                    }
+                                }
+                                else
+                                {
+                                    printf("%s: conf.core = %d, but it is error\n", __FUNCTION__, conf.core);
+                                }
+                                nonce_index = 0;
+                                OpenCoreNum1 = conf.OpenCoreNum1;
+                                OpenCoreNum2 = conf.OpenCoreNum2;
+                                OpenCoreNum3 = conf.OpenCoreNum3;
+                                OpenCoreNum4 = conf.OpenCoreNum4;
+
+                                for(n=0; n<whose_nonce; n++)
+                                {
+                                    if(n < 32)
+                                    {
+                                        if(OpenCoreNum1 & 0x00000001)
+                                        {
+                                            nonce_index++;
+                                            OpenCoreNum1 = OpenCoreNum1 >> 1;
+                                        }
+                                        else
+                                        {
+                                            OpenCoreNum1 = OpenCoreNum1 >> 1;
+                                        }
+                                    }
+                                    else if((n >= 32) && (n < 64))
+                                    {
+                                        if(OpenCoreNum2 & 0x00000001)
+                                        {
+                                            nonce_index++;
+                                            OpenCoreNum2 = OpenCoreNum2 >> 1;
+                                        }
+                                        else
+                                        {
+                                            OpenCoreNum2 = OpenCoreNum2 >> 1;
+                                        }
+                                    }
+                                    else if((n >= 64) && (n < 96))
+                                    {
+                                        if(OpenCoreNum3 & 0x00000001)
+                                        {
+                                            nonce_index++;
+                                            OpenCoreNum3 = OpenCoreNum3 >> 1;
+                                        }
+                                        else
+                                        {
+                                            OpenCoreNum3 = OpenCoreNum3 >> 1;
+                                        }
+                                    }
+                                    else
+                                    {
+                                        if(OpenCoreNum4 & 0x00000001)
+                                        {
+                                            nonce_index++;
+                                            OpenCoreNum4 = OpenCoreNum4 >> 1;
+                                        }
+                                        else
+                                        {
+                                            OpenCoreNum4 = OpenCoreNum4 >> 1;
+                                        }
+                                    }
+                                }
+                                //printf("%s: nonce_index = 0x%08x\n", __FUNCTION__, nonce_index);
+                            }
+                            else
+                            {
+                                if(CHIP_ADDR_INTERVAL != 0)
+                                {
+                                    which_asic_nonce = (buf[1] >> 24) / CHIP_ADDR_INTERVAL;
+                                    if(which_asic_nonce >= CHAIN_ASIC_NUM)
+                                    {
+                                        continue;
+                                    }
+                                    //printf("%s: which_asic = %d\n", __FUNCTION__, which_asic);
+                                }
+                                else
+                                {
+                                    //printf("CHIP_ADDR_INTERVAL==0, default=4\n");
+                                    which_asic_nonce = (buf[1] >> 24) / 4;
+                                    if(which_asic_nonce >= conf.asicNum)
+                                    {
+                                        continue;
+                                    }
+                                }
+                                whose_nonce = which_asic_nonce;
+                                nonce_index = which_asic_nonce;
+                            }
+                            //printf("%s: whose_nonce = 0x%08x\n", __FUNCTION__, whose_nonce);
+
+                            llast_nonce[chainIndex] = last_nonce[chainIndex];
+                            last_nonce[chainIndex] = buf[1];
+
+                            if(work_id[chainIndex]>=MAX_WORK)
+                                continue;
+
+                            m_nonce[chainIndex] = (cgpu.works[nonce_index] + work_id[chainIndex])->nonce;
+                            //printf("%s: m_nonce = 0x%08x\n", __FUNCTION__, m_nonce);
+
+                            if(buf[1] == m_nonce[chainIndex])
+                            {
+                                //printf("%s: repeated_nonce_id[which_asic] = 0x%08x\n", __FUNCTION__, repeated_nonce_id[which_asic]);
+
+                                if(work_id[chainIndex] != repeated_nonce_id[chainIndex][whose_nonce])
+                                {
+                                    repeated_nonce_id[chainIndex][whose_nonce] = work_id[chainIndex];
+                                    asic_nonce_num[chainIndex][whose_nonce]++;
+                                    valid_nonce_num[chainIndex]++;
+
+                                    total_valid_nonce_num++;    // used to check and wait all nonce back...
+
+                                    if(cgpu.real_asic_num != 1)
+                                    {
+                                        if(conf.core <= 64)
+                                        {
+                                            which_core_nonce = (buf[1] & 0x0000003f);
+                                        }
+                                        else if((conf.core <= 128) && (conf.core > 64))
+                                        {
+                                            which_core_nonce = (buf[1] & 0x0000007f);
+                                            if(which_core_nonce <= 56)
+                                            {
+                                                which_core_nonce = which_core_nonce;
+                                            }
+                                            else if((which_core_nonce >= 64) && (which_core_nonce < 128))
+                                            {
+                                                which_core_nonce = which_core_nonce - 7;
+                                            }
+                                        }
+                                        else
+                                        {
+                                            printf("%s: conf.core = %d, but it is error\n", __FUNCTION__, conf.core);
+                                        }
+                                        asic_core_nonce_num[chainIndex][whose_nonce][which_core_nonce]++;
+                                    }
+
+                                }
+                                else
+                                {
+                                    repeated_nonce_num[chainIndex]++;
+                                    //printf("repeat nonce 0x%08x\n", buf[1]);
+                                }
+                            }
+                            else
+                            {
+                                err_nonce_num[chainIndex]++;
+                                //printf("error nonce 0x%08x\n", buf[1]);
+                            }
+                            //printf("\n");
+                        }
+                    }
+                }
+                else    //reg value
+                {
+                    insert_reg_data(buf);   // insert to driver-btm-c5.c reg buffer
+                }
+            }
+        }
+        else usleep(1000);
+    }
+
+    receiveExit=true;
+    return 0;
+}
+
+static bool doTestBoard(int test_times)
+{
+    int i, freq_index = 0;
+    char logstr[1024];
+    int wait_count=0;
+    int last_send_num;
+    int last_recv_num;
+    int vol_value, vol_pic, vol_value_limited;
+    bool result_flag=true;
+
+    memset(asic_nonce_num, 0, sizeof(asic_nonce_num));
+    memset(asic_core_nonce_num, 0, sizeof(asic_core_nonce_num));
+    memset(repeated_nonce_id, 0xff, sizeof(repeated_nonce_id));
+    memset(err_nonce_num, 0, sizeof(err_nonce_num));
+    memset(last_nonce_num, 0, sizeof(last_nonce_num));
+    memset(repeated_nonce_num, 0, sizeof(repeated_nonce_num));
+    memset(valid_nonce_num, 0, sizeof(valid_nonce_num));
+    memset(send_work_num, 0, sizeof(send_work_num));
+
+    total_valid_nonce_num=0;
+
+    start_receive=true;
+
+    FOR_LOOP_CHAIN
+    {
+        cgpu.chain_exist[i] = getChainExistFlag(i);
+    }
+
+#ifndef T9_18
+    sprintf(logstr,"Check voltage total rate=%d\n",GetTotalRate());
+    writeLogFile(logstr);
+
+    for(i=0; i < BITMAIN_MAX_CHAIN_NUM; i++)  // here must use i from 0 in for loop, because we use j to get the index as config file's voltage value
+    {
+        if(cgpu.chain_exist[i]==0)
+            continue;
+
+        pthread_mutex_lock(&iic_mutex);
+        vol_pic=get_pic_voltage(i);
+        pthread_mutex_unlock(&iic_mutex);
+
+        vol_value = getVolValueFromPICvoltage(vol_pic);
+
+        chain_vol_value[i]=(vol_value/10)*10;   // must record current voltage!!!
+
+        vol_value_limited=getVoltageLimitedFromHashrate(GetTotalRate());
+
+        sprintf(logstr,"get PIC voltage=%d [%d] on chain[%d], check: must be < %d\n",chain_vol_value[i],vol_pic,i,vol_value_limited);
+        writeLogFile(logstr);
+
+        if(chain_vol_value[i] > vol_value_limited)  // we will set voltage to the highest voltage for the last chance on test patten
+        {
+            chain_vol_value[i]=vol_value_limited;
+            sprintf(logstr,"will set the voltage limited on chain[%d], change voltage=%d\n",i,chain_vol_value[i]);
+            writeLogFile(logstr);
+
+            vol_pic=getPICvoltageFromValue(chain_vol_value[i]);
+            sprintf(logstr,"now set pic voltage=%d on chain[%d]\n",vol_pic,i);
+            writeLogFile(logstr);
+
+            pthread_mutex_lock(&iic_mutex);
+            set_pic_voltage(i, vol_pic);
+            pthread_mutex_unlock(&iic_mutex);
+
+            someBoardUpVoltage=true;
+        }
+    }
+#endif
+
+    reset_work_data();
+
+    cgpu.CommandMode = 0;
+    cgpu.AsicType = ASIC_TYPE;
+    cgpu.asicNum = conf.asicNum;
+    cgpu.real_asic_num = CHAIN_ASIC_NUM;
+    cgpu.core_num = conf.core;
+
+    pthread_mutex_lock(&opencore_readtemp_mutex);
+    FOR_LOOP_CHAIN
+    {
+        if(cgpu.chain_exist[i]==0)
+            continue;
+
+        cgpu.chain_asic_num[i]=getChainAsicNum(i);
+
+        if(chain_need_opencore[i])
+        {
+            sprintf(logstr,"do open core on Chain[%d]...\n",i);
+            writeLogFile(logstr);
+
+            open_core_one_chain(i,true);
+
+            sprintf(logstr,"Done open core on Chain[%d]!\n",i);
+            writeLogFile(logstr);
+        }
+    }
+    pthread_mutex_unlock(&opencore_readtemp_mutex);
+
+    // before the first time for sending work, reset the FPGA's nonce fifo
+    if(!gBegin_get_nonce)
+    {
+        //printf("\n--- clear nonce fifo before send work\n");
+        printf("clement2 set_nonce_fifo_interrupt\n");
+        set_nonce_fifo_interrupt(get_nonce_fifo_interrupt() | FLUSH_NONCE3_FIFO);
+        gBegin_get_nonce = true;
+    }
+
+    FOR_LOOP_CHAIN
+    {
+        if(cgpu.chain_exist[i]==0)
+            continue;
+
+        sprintf(logstr,"start send works on chain[%d]\n",i);
+        writeLogFile(logstr);
+
+        StartSendFlag[i]=true;
+    }
+
+    send_func_all();
+
+    for(i=0; i < BITMAIN_MAX_CHAIN_NUM; i++)
+    {
+        if(cgpu.chain_exist[i]==0)
+            continue;
+
+        sprintf(logstr,"wait recv nonce on chain[%d]\n",i);
+        writeLogFile(logstr);
+
+        last_recv_num=0;
+        wait_count=0;
+        while(wait_count < RECV_WAIT_TIMEOUT && valid_nonce_num[i]<chain_ValidNonce[i])
+        {
+            if(last_recv_num!=valid_nonce_num[i])
+            {
+                wait_count=0;
+                last_recv_num=valid_nonce_num[i];
+            }
+            else wait_count++;
+
+            usleep(100000);
+        }
+    }
+
+    gBegin_get_nonce=false;
+    start_receive=false;
+
+    FOR_LOOP_CHAIN
+    {
+        if(cgpu.chain_exist[i]==0)
+            continue;
+
+        sprintf(logstr,"get nonces on chain[%d]\n",i);
+        writeLogFile(logstr);
+
+        result = get_result(i, chain_PassCount[i], chain_ValidNonce[i]);
+    }
+
+    result_flag=true;
+
+    FOR_LOOP_CHAIN
+    {
+        if(cgpu.chain_exist[i]==0)
+            continue;
+
+        if(last_all_core_opened(i))
+        {
+            sprintf(logstr,"chain[%d]: All chip cores are opened OK!\n",i);
+            writeLogFile(logstr);
+
+            chain_need_opencore[i]=false;
+
+            isChainAllCoresOpened[i]=true;
+        }
+        else
+        {
+            sprintf(logstr,"chain[%d]: some chip cores are not opened FAILED!\n",i);
+            writeLogFile(logstr);
+
+            chain_need_opencore[i]=true;    // next time , force to re-open core again if open core failed!
+
+            isChainAllCoresOpened[i]=false;
+        }
+
+        if(last_all_pass(i))
+        {
+            sprintf(logstr,"Test Patten on chain[%d]: OK!\n",i);
+            writeLogFile(logstr);
+        }
+        else
+        {
+            result_flag=false;
+
+            sprintf(logstr,"Test Patten on chain[%d]: FAILED!\n",i);
+            writeLogFile(logstr);
+
+#ifndef T9_18
+#ifdef CHECK_ALLNONCE_ADD_VOLTAGE_USERMODE
+            if(readRestartNum()>0 && isChainEnough())   // up voltage is not suitable for T9+
+            {
+                sprintf(logstr,"Try to add voltage on chain[%d]...\n",i);
+                writeLogFile(logstr);
+
+                vol_value=getVoltageLimitedFromHashrate(GetTotalRate());
+
+                if(test_times>=PRE_HEAT_TEST_COUNT-1 && chain_vol_value[i]<vol_value)   // we will set voltage to the highest voltage for the last chance on test patten
+                {
+                    chain_vol_value[i]=vol_value;
+                    sprintf(logstr,"will set the voltage limited on chain[%d], change voltage=%d\n",i,chain_vol_value[i]);
+                    writeLogFile(logstr);
+
+                    vol_pic=getPICvoltageFromValue(chain_vol_value[i]);
+                    sprintf(logstr,"now set pic voltage=%d on chain[%d]\n",vol_pic,i);
+                    writeLogFile(logstr);
+
+                    pthread_mutex_lock(&iic_mutex);
+                    set_pic_voltage(i, vol_pic);
+                    pthread_mutex_unlock(&iic_mutex);
+
+                    someBoardUpVoltage=true;
+                }
+                else if(chain_vol_value[i]+10<=vol_value)
+                {
+                    chain_vol_value[i]+=10;
+                    sprintf(logstr,"Can add 0.1V on chain[%d], change voltage=%d\n",i,chain_vol_value[i]);
+                    writeLogFile(logstr);
+
+                    vol_pic=getPICvoltageFromValue(chain_vol_value[i]);
+                    sprintf(logstr,"now set pic voltage=%d on chain[%d]\n",vol_pic,i);
+                    writeLogFile(logstr);
+
+                    pthread_mutex_lock(&iic_mutex);
+                    set_pic_voltage(i, vol_pic);
+                    pthread_mutex_unlock(&iic_mutex);
+
+                    someBoardUpVoltage=true;
+                }
+            }
+#endif
+#endif
+            search_freq_result[i]=false;
+        }
+    }
+
+    return result_flag;
+}
+
+static bool showLogToKernelLog=true;
+static void writeLogFile(char *logstr)
+{
+    if(showLogToKernelLog)
+        writeInitLogFile(logstr);
+}
+
+static int init_once=1;
+
+bool clement_doTestBoard(bool showlog)
+{
+    int run_count = 0;
+    int ret, i,j,k;
+    char logstr[1024];
+    bool doOnce=true;
+    int wait_count;
+    int rebootTestNum;  // for searching process, to reboot 3times to check hashrate.
+    int restartMinerNum;    // the number of chances to reboot miner, for sometime hashrate is low when first startup.
+    bool result_flag;
+
+    showLogToKernelLog=showlog;
+
+    if(init_once>0)
+    {
+        ret = cgpu_init();
+        if(ret < 0)
+        {
+            printf("cgpu_init Error!\n");
+            return false;
+        }
+
+        ret = configMiner();
+        if(ret < 0)
+        {
+            printf("configMiner Error!\n");
+            return false;
+        }
+
+        init_once=0;
+
+        printf("single board test start\n");
+
+        Conf.DataCount=conf.dataCount=TESTMODE_PATTEN_NUM;  // fixed to 114
+        Conf.PassCount1=conf.passCount1=TESTMODE_PATTEN_NUM;
+        Conf.PassCount2=conf.passCount2=TESTMODE_PATTEN_NUM;
+        Conf.PassCount3=conf.passCount3=TESTMODE_PATTEN_NUM;
+        Conf.ValidNonce1=conf.ValidNonce1=TESTMODE_NONCE_NUM;
+        Conf.ValidNonce2=conf.ValidNonce2=TESTMODE_NONCE_NUM;
+        Conf.ValidNonce3=conf.ValidNonce3=TESTMODE_NONCE_NUM;
+
+        ExitFlag=false;
+
+        receiveExit=false;
+        pthread_create(&cgpu.receive_id, NULL, receive_func, &cgpu);
+
+        for(i=0; i<BITMAIN_MAX_CHAIN_NUM; i++)
+        {
+            StartSendFlag[i]=false;
+        }
+    }
+
+    for(i=0; i<BITMAIN_MAX_CHAIN_NUM; i++)
+    {
+        testModeOKCounter[i]=0;
+        for(j=0; j<256; j++)
+        {
+            last_result[i][j] = 0 ;
+            last_result_opencore[i][j] = 0 ;
+        }
+
+        // force to test all boards at any time
+        chain_vol_value[i]=0;
+        chain_vol_final[i]=0;
+        chain_vol_added[i]=0;
+        search_freq_result[i]=true;
+
+        chain_DataCount[i]=TESTMODE_PATTEN_NUM; // when seaching base freq, we use 8*144 patten on chip
+        chain_ValidNonce[i]=TESTMODE_NONCE_NUM;
+        chain_PassCount[i]=TESTMODE_PATTEN_NUM;
+
+        chain_need_opencore[i]=false;   // init must be false, because chip cores are opened in driver-btm-c5.c
+    }
+
+    k=0;
+    do
+    {
+        k++;
+
+        sprintf(logstr,"do heat board 8xPatten for %d times\n",k);
+        writeLogFile(logstr);
+
+        for(i=0; i<BITMAIN_MAX_CHAIN_NUM; i++)
+        {
+            for(j=0; j<256; j++)
+            {
+                last_result[i][j] = 0 ;
+                last_result_opencore[i][j] = 0 ;
+            }
+
+            // force to test all boards at any time
+            search_freq_result[i]=true;
+
+            chain_DataCount[i]=TESTMODE_PATTEN_NUM; // when seaching base freq, we use 8*144 patten on chip
+            chain_ValidNonce[i]=TESTMODE_NONCE_NUM;
+            chain_PassCount[i]=TESTMODE_PATTEN_NUM;
+        }
+
+        result_flag=doTestBoard(k);
+
+        for(i=0; i < BITMAIN_MAX_CHAIN_NUM; i++)
+        {
+            if(cgpu.chain_exist[i]==0)
+                continue;
+
+            if(search_freq_result[i])
+            {
+                testModeOKCounter[i]++;
+            }
+        }
+
+        if(result_flag) // if test paten OK, we stop preheat
+            break;
+    }
+    while(k<PRE_HEAT_TEST_COUNT);
+
+    if(isAllChainChipCoreOpened())
+    {
+        result_flag=true;
+        someBoardUpVoltage=false;   // if all chip core opened, then we do not re-init again!!!
+    }
+    else
+    {
+        result_flag=false;
+        someBoardUpVoltage=true;
+    }
+
+    set_PWM(100);   // when exit preheat, set full speed of fan
+    return result_flag;
+}
+
+bool clement_doTestBoardOnce(bool showlog)
+{
+    int run_count = 0;
+    int ret, i,j;
+    char logstr[1024];
+    bool doOnce=true;
+    int wait_count;
+    int rebootTestNum;  // for searching process, to reboot 3times to check hashrate.
+    int restartMinerNum;    // the number of chances to reboot miner, for sometime hashrate is low when first startup.
+
+    showLogToKernelLog=showlog;
+
+    if(init_once>0)
+    {
+        ret = cgpu_init();
+        if(ret < 0)
+        {
+            printf("cgpu_init Error!\n");
+            return false;
+        }
+
+        ret = configMiner();
+        if(ret < 0)
+        {
+            printf("configMiner Error!\n");
+            return false;
+        }
+
+        init_once=0;
+
+        printf("single board test start\n");
+
+        Conf.DataCount=conf.dataCount=TESTMODE_PATTEN_NUM;  // fixed to 114
+        Conf.PassCount1=conf.passCount1=TESTMODE_PATTEN_NUM;
+        Conf.PassCount2=conf.passCount2=TESTMODE_PATTEN_NUM;
+        Conf.PassCount3=conf.passCount3=TESTMODE_PATTEN_NUM;
+        Conf.ValidNonce1=conf.ValidNonce1=TESTMODE_NONCE_NUM;
+        Conf.ValidNonce2=conf.ValidNonce2=TESTMODE_NONCE_NUM;
+        Conf.ValidNonce3=conf.ValidNonce3=TESTMODE_NONCE_NUM;
+
+        ExitFlag=false;
+
+        receiveExit=false;
+        pthread_create(&cgpu.receive_id, NULL, receive_func, &cgpu);
+
+        for(i=0; i<BITMAIN_MAX_CHAIN_NUM; i++)
+        {
+            StartSendFlag[i]=false;
+        }
+    }
+
+    for(i=0; i<BITMAIN_MAX_CHAIN_NUM; i++)
+    {
+        testModeOKCounter[i]=0;
+        for(j=0; j<256; j++)
+        {
+            last_result[i][j] = 0 ;
+            last_result_opencore[i][j] = 0 ;
+        }
+
+        // force to test all boards at any time
+        chain_vol_value[i]=0;
+        chain_vol_final[i]=0;
+        chain_vol_added[i]=0;
+        search_freq_result[i]=true;
+
+        chain_DataCount[i]=TESTMODE_PATTEN_NUM; // when seaching base freq, we use 8*144 patten on chip
+        chain_ValidNonce[i]=TESTMODE_NONCE_NUM;
+        chain_PassCount[i]=TESTMODE_PATTEN_NUM;
+
+        chain_need_opencore[i]=false;   // init must be false, because chip cores are opened in driver-btm-c5.c
+    }
+
+    doTestBoard(0);
+
+    for(i=0; i < BITMAIN_MAX_CHAIN_NUM; i++)
+    {
+        if(cgpu.chain_exist[i]==0)
+            continue;
+
+        if(search_freq_result[i])
+        {
+            testModeOKCounter[i]++;
+        }
+    }
+
+    set_PWM(100);   // when exit preheat, set full speed of fan
+    return true;
+}
+
+

+ 244 - 0
bitmain-board-test.h

@@ -0,0 +1,244 @@
+/*
+ * Copyright 2016-2017 Fazio Bai <yang.bai@bitmain.com>
+ * Copyright 2016-2017 Clement Duan <kai.duan@bitmain.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+#ifndef __BITMAIN_BOARD_TEST_H__
+#define __BITMAIN_BOARD_TEST_H__
+#include <stdbool.h>
+
+#define MAX_ASIC_NUM 128
+#define MAX_WORK 5000
+
+#define FIL 0x1
+#define VIL 0x0
+
+struct configuration
+{
+    bool AutoStart;
+    bool Gray;
+    int NonceMask; //???
+    int DataCount;
+    int PassCount1;
+    int PassCount2;
+    int PassCount3;
+    int Freq;
+    int Timeout;
+    bool Regulate;
+    int Value;
+    int ReadIntervalTimeout;
+    int AddrInterval;
+    int CoreNum;
+    int AsicNum;
+    int UseFreqPIC;
+    int TestMode;
+    int CheckChain;
+    int CommandMode;
+    int AsicType;
+    int ValidNonce1;
+    int ValidNonce2;
+    int ValidNonce3;
+    unsigned int Pic;
+    unsigned int Voltage1;
+    unsigned int Voltage2;
+    unsigned int Voltage3;
+    unsigned int final_voltage1;
+    unsigned int final_voltage2;
+    unsigned int final_voltage3;
+    unsigned int freq_gap;
+    int OpenCoreGap;
+    int checktemp;
+    unsigned int IICPic;
+    unsigned int OpenCoreNum1;
+    unsigned int OpenCoreNum2;
+    unsigned int OpenCoreNum3;
+    unsigned int OpenCoreNum4;
+    unsigned int dac;
+    unsigned int GetTempFrom;
+    unsigned int TempSel;
+    unsigned int TempSensor1;
+    unsigned int TempSensor2;
+    unsigned int TempSensor3;
+    unsigned int TempSensor4;
+    signed char DefaultTempOffset;
+    int freq_e;
+    int freq_m;
+    int freq_a;
+    int freq_t;
+    int force_freq;
+    int UseConfigVol;
+    int StartSensor;
+    int StartTemp;
+    int year;
+    int month;
+    int date;
+    int hour;
+    int minute;
+    int second;
+};
+
+struct _CONFIG
+{
+    int dataCount;
+    int passCount1;
+    int passCount2;
+    int passCount3;
+    int core;
+    int freq;
+    int timeout;
+    int baud;
+    bool regulate;
+    int value;
+    int addrInterval;
+    int asicNum;
+    int testMode;
+    int CommandMode;
+    int AsicType;
+    int ValidNonce1;
+    int ValidNonce2;
+    int ValidNonce3;
+    unsigned int Pic;
+    unsigned int Voltage1;
+    unsigned int Voltage2;
+    unsigned int Voltage3;
+    int OpenCoreGap;
+    int checktemp;
+    unsigned int IICPic;
+    int UseFreqPIC;
+    unsigned int freq_gap;
+    unsigned int OpenCoreNum1;
+    unsigned int OpenCoreNum2;
+    unsigned int OpenCoreNum3;
+    unsigned int OpenCoreNum4;
+    unsigned int dac;
+    unsigned int GetTempFrom;
+    unsigned int TempSel;
+    unsigned char TempSensor1;
+    unsigned char TempSensor2;
+    unsigned char TempSensor3;
+    unsigned char TempSensor4;
+    signed char DefaultTempOffset;
+    int freq_e;
+    int freq_m;
+    int freq_a;
+    int freq_t;
+    int force_freq;
+    int UseConfigVol;
+    unsigned char StartSensor;
+    signed char StartTemp;
+    int year;
+    int month;
+    int date;
+    int hour;
+    int minute;
+    int second;
+};
+
+struct testpatten_work
+{
+    int     id;
+    uint32_t nonce; /* For devices that hash sole work */
+    unsigned char data[12];
+    unsigned char   midstate[32];
+};
+
+struct testpatten_cgpu_info
+{
+    FILE * fps[MAX_ASIC_NUM];
+
+    pthread_t receive_id, show_id, pic_heart_beat_id, read_temp,freq_id;
+    pthread_t send_id[BITMAIN_MAX_CHAIN_NUM];
+    int device_fd;
+    int lcd_fd;
+
+    char workdataPathPrefix[64];
+    struct testpatten_work *works[MAX_ASIC_NUM];
+    //int work_array[MAX_ASIC_NUM]; //work number of every asic
+    uint32_t results[MAX_ASIC_NUM][MAX_WORK];
+    int result_array[MAX_ASIC_NUM]; //return nonce number of every asic
+    int subid[MAX_ASIC_NUM];
+    int min_work_subid;
+    int index;
+    int valid_nonce;
+    int err_nonce;
+    int repeated_nonce;
+
+    int start_key_fd;
+    int red_led_fd;
+    int green_led_fd;
+    int beep_fd;
+
+    unsigned int    real_asic_num;
+    unsigned int    asicNum;
+    unsigned int    core_num;
+    int freq_e;
+    int freq_m;
+    int freq_a;
+    int freq_t;
+    int AsicType;
+    unsigned int    chain_num;
+    unsigned short int  frequency;
+    unsigned int    CommandMode;    // 1:fil  0:vil
+    unsigned int    chain_exist[BITMAIN_MAX_CHAIN_NUM];
+    unsigned int    timeout;
+    unsigned char   chain_asic_num[BITMAIN_MAX_CHAIN_NUM];
+    unsigned int   addrInterval;
+    unsigned char   baud;
+    unsigned short int  freq[BITMAIN_MAX_CHAIN_NUM];
+    unsigned int max_asic_num_in_one_chain;
+    unsigned char temp_sel;
+    unsigned char rfs;
+    unsigned char tfs;
+    signed char T1_offset_value;
+    signed char T2_offset_value;
+    signed char T3_offset_value;
+    signed char T4_offset_value;
+};
+
+#define PRE_HEAT_TEST_COUNT     2
+
+#undef CHECK_ALLNONCE_ADD_VOLTAGE_USERMODE //if defined, when in user mode (restartNum>0), then check nonce num in test patten, if failed, add 0.1V voltage , must according to hashrate
+
+#define RETRY_FREQ_INDEX    12  // 400M, if search base freq < 400M, will switch to use RETRY_VOLTAGE to search again.
+#define REBOOT_TEST_NUM     2   // save into file
+
+#define ENABLE_SEARCH_LOGFILE   //enable log info into kernel info web page.
+#define LOG_CHIPS_CORE_DETAIL   // if enabled , will show details nonce number info for chips and cores, open for debug
+
+#define TEST_MODE_OK_NUM    2   // if 8xPatten test mode test OK counter >= TEST_MODE_OK_NUM, then this board is OK,  3 
+
+#define SEARCH_FREQ_CHANCE_NUM  2   // give each board 2 chances to search freq, the first failed, we can add voltage  SEARCH_VOLTAGE_ADD_STEP to search for next chance
+#define SEARCH_VOLTAGE_ADD_STEP 30  // means, each chance will add 0.3V to search freq again.
+
+#define SEARCH_BASEFREQ_PATTEN_NUM  912
+#define SEARCH_BASEFREQ_NONCE_NUM   (SEARCH_BASEFREQ_PATTEN_NUM*CHAIN_ASIC_NUM)
+
+#define SEARCH_FREQ_PATTEN_NUM      114
+#define SEARCH_FREQ_NONCE_NUM       (SEARCH_FREQ_PATTEN_NUM*CHAIN_ASIC_NUM)
+
+#define TESTMODE_PATTEN_NUM         912
+#define TESTMODE_NONCE_NUM          (TESTMODE_PATTEN_NUM*CHAIN_ASIC_NUM)
+
+#define DEFAULT_TEMP_OFFSET     -70
+
+#define FOR_LOOP_CHAIN  for(i=0; i<BITMAIN_MAX_CHAIN_NUM; i++)
+
+#define LOWEST_FREQ_INDEX   4   // 8:300M       6:250M      4:200M
+#define HIGHEST_FREQ_INDEX  100 // 850M:100 700M:82  668M:77
+
+#define SEND_WAIT_TIMEOUT   120 // unit is 100ms
+#define RECV_WAIT_TIMEOUT   20 // unit is 100ms
+
+#define NOBOARD_RETRY_COUNT 3
+
+#ifdef ALLOW_KPERCENT_8xPATTEN
+static void fix_result_byPercent(int chainIndex);
+#endif
+
+static int calculate_core_number(unsigned int actual_core_number);
+
+#endif

+ 23 - 0
bitstreams/COPYING_fpgaminer

@@ -0,0 +1,23 @@
+All the bitstream files included in this directory that follow the name pattern fpgaminer_*.ncd are:
+
+----
+
+Copyright (c) 2011-2012 fpgaminer@bitcoin-mining.com
+
+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.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+----
+
+You can find the original sources at the Open Source FPGA Bitcoin Miner project GitHub repository:
+https://github.com/progranism/Open-Source-FPGA-Bitcoin-Miner/tree/master/projects/X6000_ztex_comm4/hdl

+ 1 - 0
bitstreams/README

@@ -0,0 +1 @@
+You must put the file fpgaminer_top_fixed7_197MHz.ncd in here for modminer to work.

BIN
bitstreams/fpgaminer_top_fixed7_197MHz.ncd


+ 271 - 0
c++defs.h

@@ -0,0 +1,271 @@
+/* C++ compatible function declaration macros.
+   Copyright (C) 2010-2011 Free Software Foundation, Inc.
+
+   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.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _GL_CXXDEFS_H
+#define _GL_CXXDEFS_H
+
+/* The three most frequent use cases of these macros are:
+
+   * For providing a substitute for a function that is missing on some
+     platforms, but is declared and works fine on the platforms on which
+     it exists:
+
+       #if @GNULIB_FOO@
+       # if !@HAVE_FOO@
+       _GL_FUNCDECL_SYS (foo, ...);
+       # endif
+       _GL_CXXALIAS_SYS (foo, ...);
+       _GL_CXXALIASWARN (foo);
+       #elif defined GNULIB_POSIXCHECK
+       ...
+       #endif
+
+   * For providing a replacement for a function that exists on all platforms,
+     but is broken/insufficient and needs to be replaced on some platforms:
+
+       #if @GNULIB_FOO@
+       # if @REPLACE_FOO@
+       #  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+       #   undef foo
+       #   define foo rpl_foo
+       #  endif
+       _GL_FUNCDECL_RPL (foo, ...);
+       _GL_CXXALIAS_RPL (foo, ...);
+       # else
+       _GL_CXXALIAS_SYS (foo, ...);
+       # endif
+       _GL_CXXALIASWARN (foo);
+       #elif defined GNULIB_POSIXCHECK
+       ...
+       #endif
+
+   * For providing a replacement for a function that exists on some platforms
+     but is broken/insufficient and needs to be replaced on some of them and
+     is additionally either missing or undeclared on some other platforms:
+
+       #if @GNULIB_FOO@
+       # if @REPLACE_FOO@
+       #  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+       #   undef foo
+       #   define foo rpl_foo
+       #  endif
+       _GL_FUNCDECL_RPL (foo, ...);
+       _GL_CXXALIAS_RPL (foo, ...);
+       # else
+       #  if !@HAVE_FOO@   or   if !@HAVE_DECL_FOO@
+       _GL_FUNCDECL_SYS (foo, ...);
+       #  endif
+       _GL_CXXALIAS_SYS (foo, ...);
+       # endif
+       _GL_CXXALIASWARN (foo);
+       #elif defined GNULIB_POSIXCHECK
+       ...
+       #endif
+*/
+
+/* _GL_EXTERN_C declaration;
+   performs the declaration with C linkage.  */
+#if defined __cplusplus
+# define _GL_EXTERN_C extern "C"
+#else
+# define _GL_EXTERN_C extern
+#endif
+
+/* _GL_FUNCDECL_RPL (func, rettype, parameters_and_attributes);
+   declares a replacement function, named rpl_func, with the given prototype,
+   consisting of return type, parameters, and attributes.
+   Example:
+     _GL_FUNCDECL_RPL (open, int, (const char *filename, int flags, ...)
+                                  _GL_ARG_NONNULL ((1)));
+ */
+#define _GL_FUNCDECL_RPL(func,rettype,parameters_and_attributes) \
+  _GL_FUNCDECL_RPL_1 (rpl_##func, rettype, parameters_and_attributes)
+#define _GL_FUNCDECL_RPL_1(rpl_func,rettype,parameters_and_attributes) \
+  _GL_EXTERN_C rettype rpl_func parameters_and_attributes
+
+/* _GL_FUNCDECL_SYS (func, rettype, parameters_and_attributes);
+   declares the system function, named func, with the given prototype,
+   consisting of return type, parameters, and attributes.
+   Example:
+     _GL_FUNCDECL_SYS (open, int, (const char *filename, int flags, ...)
+                                  _GL_ARG_NONNULL ((1)));
+ */
+#define _GL_FUNCDECL_SYS(func,rettype,parameters_and_attributes) \
+  _GL_EXTERN_C rettype func parameters_and_attributes
+
+/* _GL_CXXALIAS_RPL (func, rettype, parameters);
+   declares a C++ alias called GNULIB_NAMESPACE::func
+   that redirects to rpl_func, if GNULIB_NAMESPACE is defined.
+   Example:
+     _GL_CXXALIAS_RPL (open, int, (const char *filename, int flags, ...));
+ */
+#define _GL_CXXALIAS_RPL(func,rettype,parameters) \
+  _GL_CXXALIAS_RPL_1 (func, rpl_##func, rettype, parameters)
+#if defined __cplusplus && defined GNULIB_NAMESPACE
+# define _GL_CXXALIAS_RPL_1(func,rpl_func,rettype,parameters) \
+    namespace GNULIB_NAMESPACE                                \
+    {                                                         \
+      rettype (*const func) parameters = ::rpl_func;          \
+    }                                                         \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#else
+# define _GL_CXXALIAS_RPL_1(func,rpl_func,rettype,parameters) \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#endif
+
+/* _GL_CXXALIAS_RPL_CAST_1 (func, rpl_func, rettype, parameters);
+   is like  _GL_CXXALIAS_RPL_1 (func, rpl_func, rettype, parameters);
+   except that the C function rpl_func may have a slightly different
+   declaration.  A cast is used to silence the "invalid conversion" error
+   that would otherwise occur.  */
+#if defined __cplusplus && defined GNULIB_NAMESPACE
+# define _GL_CXXALIAS_RPL_CAST_1(func,rpl_func,rettype,parameters) \
+    namespace GNULIB_NAMESPACE                                     \
+    {                                                              \
+      rettype (*const func) parameters =                           \
+        reinterpret_cast<rettype(*)parameters>(::rpl_func);        \
+    }                                                              \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#else
+# define _GL_CXXALIAS_RPL_CAST_1(func,rpl_func,rettype,parameters) \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#endif
+
+/* _GL_CXXALIAS_SYS (func, rettype, parameters);
+   declares a C++ alias called GNULIB_NAMESPACE::func
+   that redirects to the system provided function func, if GNULIB_NAMESPACE
+   is defined.
+   Example:
+     _GL_CXXALIAS_SYS (open, int, (const char *filename, int flags, ...));
+ */
+#if defined __cplusplus && defined GNULIB_NAMESPACE
+  /* If we were to write
+       rettype (*const func) parameters = ::func;
+     like above in _GL_CXXALIAS_RPL_1, the compiler could optimize calls
+     better (remove an indirection through a 'static' pointer variable),
+     but then the _GL_CXXALIASWARN macro below would cause a warning not only
+     for uses of ::func but also for uses of GNULIB_NAMESPACE::func.  */
+# define _GL_CXXALIAS_SYS(func,rettype,parameters) \
+    namespace GNULIB_NAMESPACE                     \
+    {                                              \
+      static rettype (*func) parameters = ::func;  \
+    }                                              \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#else
+# define _GL_CXXALIAS_SYS(func,rettype,parameters) \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#endif
+
+/* _GL_CXXALIAS_SYS_CAST (func, rettype, parameters);
+   is like  _GL_CXXALIAS_SYS (func, rettype, parameters);
+   except that the C function func may have a slightly different declaration.
+   A cast is used to silence the "invalid conversion" error that would
+   otherwise occur.  */
+#if defined __cplusplus && defined GNULIB_NAMESPACE
+# define _GL_CXXALIAS_SYS_CAST(func,rettype,parameters) \
+    namespace GNULIB_NAMESPACE                          \
+    {                                                   \
+      static rettype (*func) parameters =               \
+        reinterpret_cast<rettype(*)parameters>(::func); \
+    }                                                   \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#else
+# define _GL_CXXALIAS_SYS_CAST(func,rettype,parameters) \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#endif
+
+/* _GL_CXXALIAS_SYS_CAST2 (func, rettype, parameters, rettype2, parameters2);
+   is like  _GL_CXXALIAS_SYS (func, rettype, parameters);
+   except that the C function is picked among a set of overloaded functions,
+   namely the one with rettype2 and parameters2.  Two consecutive casts
+   are used to silence the "cannot find a match" and "invalid conversion"
+   errors that would otherwise occur.  */
+#if defined __cplusplus && defined GNULIB_NAMESPACE
+  /* The outer cast must be a reinterpret_cast.
+     The inner cast: When the function is defined as a set of overloaded
+     functions, it works as a static_cast<>, choosing the designated variant.
+     When the function is defined as a single variant, it works as a
+     reinterpret_cast<>. The parenthesized cast syntax works both ways.  */
+# define _GL_CXXALIAS_SYS_CAST2(func,rettype,parameters,rettype2,parameters2) \
+    namespace GNULIB_NAMESPACE                                                \
+    {                                                                         \
+      static rettype (*func) parameters =                                     \
+        reinterpret_cast<rettype(*)parameters>(                               \
+          (rettype2(*)parameters2)(::func));                                  \
+    }                                                                         \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#else
+# define _GL_CXXALIAS_SYS_CAST2(func,rettype,parameters,rettype2,parameters2) \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#endif
+
+/* _GL_CXXALIASWARN (func);
+   causes a warning to be emitted when ::func is used but not when
+   GNULIB_NAMESPACE::func is used.  func must be defined without overloaded
+   variants.  */
+#if defined __cplusplus && defined GNULIB_NAMESPACE
+# define _GL_CXXALIASWARN(func) \
+   _GL_CXXALIASWARN_1 (func, GNULIB_NAMESPACE)
+# define _GL_CXXALIASWARN_1(func,namespace) \
+   _GL_CXXALIASWARN_2 (func, namespace)
+/* To work around GCC bug <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43881>,
+   we enable the warning only when not optimizing.  */
+# if !__OPTIMIZE__
+#  define _GL_CXXALIASWARN_2(func,namespace) \
+    _GL_WARN_ON_USE (func, \
+                     "The symbol ::" #func " refers to the system function. " \
+                     "Use " #namespace "::" #func " instead.")
+# elif __GNUC__ >= 3 && GNULIB_STRICT_CHECKING
+#  define _GL_CXXALIASWARN_2(func,namespace) \
+     extern __typeof__ (func) func
+# else
+#  define _GL_CXXALIASWARN_2(func,namespace) \
+     _GL_EXTERN_C int _gl_cxxalias_dummy
+# endif
+#else
+# define _GL_CXXALIASWARN(func) \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#endif
+
+/* _GL_CXXALIASWARN1 (func, rettype, parameters_and_attributes);
+   causes a warning to be emitted when the given overloaded variant of ::func
+   is used but not when GNULIB_NAMESPACE::func is used.  */
+#if defined __cplusplus && defined GNULIB_NAMESPACE
+# define _GL_CXXALIASWARN1(func,rettype,parameters_and_attributes) \
+   _GL_CXXALIASWARN1_1 (func, rettype, parameters_and_attributes, \
+                        GNULIB_NAMESPACE)
+# define _GL_CXXALIASWARN1_1(func,rettype,parameters_and_attributes,namespace) \
+   _GL_CXXALIASWARN1_2 (func, rettype, parameters_and_attributes, namespace)
+/* To work around GCC bug <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43881>,
+   we enable the warning only when not optimizing.  */
+# if !__OPTIMIZE__
+#  define _GL_CXXALIASWARN1_2(func,rettype,parameters_and_attributes,namespace) \
+    _GL_WARN_ON_USE_CXX (func, rettype, parameters_and_attributes, \
+                         "The symbol ::" #func " refers to the system function. " \
+                         "Use " #namespace "::" #func " instead.")
+# elif __GNUC__ >= 3 && GNULIB_STRICT_CHECKING
+#  define _GL_CXXALIASWARN1_2(func,rettype,parameters_and_attributes,namespace) \
+     extern __typeof__ (func) func
+# else
+#  define _GL_CXXALIASWARN1_2(func,rettype,parameters_and_attributes,namespace) \
+     _GL_EXTERN_C int _gl_cxxalias_dummy
+# endif
+#else
+# define _GL_CXXALIASWARN1(func,rettype,parameters_and_attributes) \
+    _GL_EXTERN_C int _gl_cxxalias_dummy
+#endif
+
+#endif /* _GL_CXXDEFS_H */

+ 4 - 0
ccan/Makefile.am

@@ -0,0 +1,4 @@
+noinst_LIBRARIES	= libccan.a
+
+libccan_a_SOURCES	= compiler/compiler.h opt/helpers.c opt/opt.c opt/opt.h opt/parse.c opt/private.h opt/usage.c typesafe_cb/typesafe_cb.h
+libccan_a_CPPFLAGS	= -I$(top_srcdir)

+ 165 - 0
ccan/compiler/LICENSE

@@ -0,0 +1,165 @@
+		   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions. 
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version. 
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.

+ 64 - 0
ccan/compiler/_info

@@ -0,0 +1,64 @@
+#include <string.h>
+#include <stdio.h>
+#include "config.h"
+
+/**
+ * compiler - macros for common compiler extensions
+ *
+ * Abstracts away some compiler hints.  Currently these include:
+ * - COLD
+ *	For functions not called in fast paths (aka. cold functions)
+ * - PRINTF_FMT
+ *	For functions which take printf-style parameters.
+ * - IDEMPOTENT
+ *	For functions which return the same value for same parameters.
+ * - NEEDED
+ *	For functions and variables which must be emitted even if unused.
+ * - UNNEEDED
+ *	For functions and variables which need not be emitted if unused.
+ * - UNUSED
+ *	For parameters which are not used.
+ * - IS_COMPILE_CONSTANT
+ *	For using different tradeoffs for compiletime vs runtime evaluation.
+ *
+ * License: LGPL (3 or any later version)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ *
+ * Example:
+ *	#include <ccan/compiler/compiler.h>
+ *	#include <stdio.h>
+ *	#include <stdarg.h>
+ *
+ *	// Example of a (slow-path) logging function.
+ *	static int log_threshold = 2;
+ *	static void COLD PRINTF_FMT(2,3)
+ *		logger(int level, const char *fmt, ...)
+ *	{
+ *		va_list ap;
+ *		va_start(ap, fmt);
+ *		if (level >= log_threshold)
+ *			vfprintf(stderr, fmt, ap);
+ *		va_end(ap);
+ *	}
+ *
+ *	int main(int argc, char *argv[])
+ *	{
+ *		if (argc != 1) {
+ *			logger(3, "Don't want %i arguments!\n", argc-1);
+ *			return 1;
+ *		}
+ *		return 0;
+ *	}
+ */
+int main(int argc, char *argv[])
+{
+	/* Expect exactly one argument */
+	if (argc != 2)
+		return 1;
+
+	if (strcmp(argv[1], "depends") == 0) {
+		return 0;
+	}
+
+	return 1;
+}

+ 216 - 0
ccan/compiler/compiler.h

@@ -0,0 +1,216 @@
+#ifndef CCAN_COMPILER_H
+#define CCAN_COMPILER_H
+#include "config.h"
+
+#ifndef COLD
+#if HAVE_ATTRIBUTE_COLD
+/**
+ * COLD - a function is unlikely to be called.
+ *
+ * Used to mark an unlikely code path and optimize appropriately.
+ * It is usually used on logging or error routines.
+ *
+ * Example:
+ * static void COLD moan(const char *reason)
+ * {
+ *	fprintf(stderr, "Error: %s (%s)\n", reason, strerror(errno));
+ * }
+ */
+#define COLD __attribute__((cold))
+#else
+#define COLD
+#endif
+#endif
+
+#ifndef NORETURN
+#if HAVE_ATTRIBUTE_NORETURN
+/**
+ * NORETURN - a function does not return
+ *
+ * Used to mark a function which exits; useful for suppressing warnings.
+ *
+ * Example:
+ * static void NORETURN fail(const char *reason)
+ * {
+ *	fprintf(stderr, "Error: %s (%s)\n", reason, strerror(errno));
+ *	exit(1);
+ * }
+ */
+#define NORETURN __attribute__((noreturn))
+#else
+#define NORETURN
+#endif
+#endif
+
+#ifndef PRINTF_FMT
+#if HAVE_ATTRIBUTE_PRINTF
+/**
+ * PRINTF_FMT - a function takes printf-style arguments
+ * @nfmt: the 1-based number of the function's format argument.
+ * @narg: the 1-based number of the function's first variable argument.
+ *
+ * This allows the compiler to check your parameters as it does for printf().
+ *
+ * Example:
+ * void PRINTF_FMT(2,3) my_printf(const char *prefix, const char *fmt, ...);
+ */
+#define PRINTF_FMT(nfmt, narg) \
+	__attribute__((format(__printf__, nfmt, narg)))
+#else
+#define PRINTF_FMT(nfmt, narg)
+#endif
+#endif
+
+#ifndef IDEMPOTENT
+#if HAVE_ATTRIBUTE_CONST
+/**
+ * IDEMPOTENT - a function's return depends only on its argument
+ *
+ * This allows the compiler to assume that the function will return the exact
+ * same value for the exact same arguments.  This implies that the function
+ * must not use global variables, or dereference pointer arguments.
+ */
+#define IDEMPOTENT __attribute__((const))
+#else
+#define IDEMPOTENT
+#endif
+#endif
+
+#if HAVE_ATTRIBUTE_UNUSED
+#ifndef UNNEEDED
+/**
+ * UNNEEDED - a variable/function may not be needed
+ *
+ * This suppresses warnings about unused variables or functions, but tells
+ * the compiler that if it is unused it need not emit it into the source code.
+ *
+ * Example:
+ * // With some preprocessor options, this is unnecessary.
+ * static UNNEEDED int counter;
+ *
+ * // With some preprocessor options, this is unnecessary.
+ * static UNNEEDED void add_to_counter(int add)
+ * {
+ *	counter += add;
+ * }
+ */
+#define UNNEEDED __attribute__((unused))
+#endif
+
+#ifndef NEEDED
+#if HAVE_ATTRIBUTE_USED
+/**
+ * NEEDED - a variable/function is needed
+ *
+ * This suppresses warnings about unused variables or functions, but tells
+ * the compiler that it must exist even if it (seems) unused.
+ *
+ * Example:
+ *	// Even if this is unused, these are vital for debugging.
+ *	static NEEDED int counter;
+ *	static NEEDED void dump_counter(void)
+ *	{
+ *		printf("Counter is %i\n", counter);
+ *	}
+ */
+#define NEEDED __attribute__((used))
+#else
+/* Before used, unused functions and vars were always emitted. */
+#define NEEDED __attribute__((unused))
+#endif
+#endif
+
+#ifndef UNUSED
+/**
+ * UNUSED - a parameter is unused
+ *
+ * Some compilers (eg. gcc with -W or -Wunused) warn about unused
+ * function parameters.  This suppresses such warnings and indicates
+ * to the reader that it's deliberate.
+ *
+ * Example:
+ *	// This is used as a callback, so needs to have this prototype.
+ *	static int some_callback(void *unused UNUSED)
+ *	{
+ *		return 0;
+ *	}
+ */
+#define UNUSED __attribute__((unused))
+#endif
+#else
+#ifndef UNNEEDED
+#define UNNEEDED
+#endif
+#ifndef NEEDED
+#define NEEDED
+#endif
+#ifndef UNUSED
+#define UNUSED
+#endif
+#endif
+
+#ifndef IS_COMPILE_CONSTANT
+#if HAVE_BUILTIN_CONSTANT_P
+/**
+ * IS_COMPILE_CONSTANT - does the compiler know the value of this expression?
+ * @expr: the expression to evaluate
+ *
+ * When an expression manipulation is complicated, it is usually better to
+ * implement it in a function.  However, if the expression being manipulated is
+ * known at compile time, it is better to have the compiler see the entire
+ * expression so it can simply substitute the result.
+ *
+ * This can be done using the IS_COMPILE_CONSTANT() macro.
+ *
+ * Example:
+ *	enum greek { ALPHA, BETA, GAMMA, DELTA, EPSILON };
+ *
+ *	// Out-of-line version.
+ *	const char *greek_name(enum greek greek);
+ *
+ *	// Inline version.
+ *	static inline const char *_greek_name(enum greek greek)
+ *	{
+ *		switch (greek) {
+ *		case ALPHA: return "alpha";
+ *		case BETA: return "beta";
+ *		case GAMMA: return "gamma";
+ *		case DELTA: return "delta";
+ *		case EPSILON: return "epsilon";
+ *		default: return "**INVALID**";
+ *		}
+ *	}
+ *
+ *	// Use inline if compiler knows answer.  Otherwise call function
+ *	// to avoid copies of the same code everywhere.
+ *	#define greek_name(g)						\
+ *		 (IS_COMPILE_CONSTANT(greek) ? _greek_name(g) : greek_name(g))
+ */
+#define IS_COMPILE_CONSTANT(expr) __builtin_constant_p(expr)
+#else
+/* If we don't know, assume it's not. */
+#define IS_COMPILE_CONSTANT(expr) 0
+#endif
+#endif
+
+#ifndef WARN_UNUSED_RESULT
+#if HAVE_WARN_UNUSED_RESULT
+/**
+ * WARN_UNUSED_RESULT - warn if a function return value is unused.
+ *
+ * Used to mark a function where it is extremely unlikely that the caller
+ * can ignore the result, eg realloc().
+ *
+ * Example:
+ * // buf param may be freed by this; need return value!
+ * static char *WARN_UNUSED_RESULT enlarge(char *buf, unsigned *size)
+ * {
+ *	return realloc(buf, (*size) *= 2);
+ * }
+ */
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define WARN_UNUSED_RESULT
+#endif
+#endif
+#endif /* CCAN_COMPILER_H */

+ 22 - 0
ccan/compiler/test/compile_fail-printf.c

@@ -0,0 +1,22 @@
+#include <ccan/compiler/compiler.h>
+
+static void PRINTF_FMT(2,3) my_printf(int x, const char *fmt, ...)
+{
+}
+
+int main(int argc, char *argv[])
+{
+	unsigned int i = 0;
+
+	my_printf(1, "Not a pointer "
+#ifdef FAIL
+		  "%p",
+#if !HAVE_ATTRIBUTE_PRINTF
+#error "Unfortunately we don't fail if !HAVE_ATTRIBUTE_PRINTF."
+#endif
+#else
+		  "%i",
+#endif
+		  i);
+	return 0;
+}

+ 15 - 0
ccan/compiler/test/run-is_compile_constant.c

@@ -0,0 +1,15 @@
+#include <ccan/compiler/compiler.h>
+#include <ccan/tap/tap.h>
+
+int main(int argc, char *argv[])
+{
+	plan_tests(2);
+
+	ok1(!IS_COMPILE_CONSTANT(argc));
+#if HAVE_BUILTIN_CONSTANT_P
+	ok1(IS_COMPILE_CONSTANT(7));
+#else
+	pass("If !HAVE_BUILTIN_CONSTANT_P, IS_COMPILE_CONSTANT always false");
+#endif
+	return exit_status();
+}

+ 339 - 0
ccan/opt/LICENSE

@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 67 - 0
ccan/opt/_info

@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * opt - simple command line parsing
+ *
+ * Simple but powerful command line parsing.
+ *
+ * Example:
+ * #include <ccan/opt/opt.h>
+ * #include <stdio.h>
+ * #include <stdlib.h>
+ * 
+ * static bool someflag;
+ * static int verbose;
+ * static char *somestring;
+ * 
+ * static struct opt_table opts[] = {
+ * 	OPT_WITHOUT_ARG("--verbose|-v", opt_inc_intval, &verbose,
+ *			"Verbose mode (can be specified more than once)"),
+ * 	OPT_WITHOUT_ARG("--someflag", opt_set_bool, &someflag,
+ *			"Set someflag"),
+ * 	OPT_WITH_ARG("--somefile=<filename>", opt_set_charp, opt_show_charp,
+ *		     &somestring, "Set somefile to <filename>"),
+ * 	OPT_WITHOUT_ARG("--usage|--help|-h", opt_usage_and_exit,
+ * 			"args...\nA silly test program.",
+ *			"Print this message."),
+ * 	OPT_ENDTABLE
+ * };
+ * 
+ * int main(int argc, char *argv[])
+ * {
+ * 	int i;
+ * 
+ * 	opt_register_table(opts, NULL);
+ * 	// For fun, register an extra one.
+ * 	opt_register_noarg("--no-someflag", opt_set_invbool, &someflag,
+ * 			   "Unset someflag");
+ * 	if (!opt_parse(&argc, argv, opt_log_stderr))
+ * 		exit(1);
+ * 
+ * 	printf("someflag = %i, verbose = %i, somestring = %s\n",
+ * 	       someflag, verbose, somestring);
+ * 	printf("%u args left over:", argc - 1);
+ * 	for (i = 1; i < argc; i++)
+ * 		printf(" %s", argv[i]);
+ * 	printf("\n");
+ * 	return 0;
+ * }
+ *
+ * License: GPL (2 or any later version)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+	if (argc != 2)
+		return 1;
+
+	if (strcmp(argv[1], "depends") == 0) {
+		printf("ccan/typesafe_cb\n");
+		printf("ccan/compiler\n");
+		return 0;
+	}
+
+	return 1;
+}

+ 199 - 0
ccan/opt/helpers.c

@@ -0,0 +1,199 @@
+#include <ccan/opt/opt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include "private.h"
+
+/* Upper bound to sprintf this simple type?  Each 3 bits < 1 digit. */
+#define CHAR_SIZE(type) (((sizeof(type)*CHAR_BIT + 2) / 3) + 1)
+
+/* FIXME: asprintf module? */
+static char *arg_bad(const char *fmt, const char *arg)
+{
+	char *str = malloc(strlen(fmt) + strlen(arg));
+	sprintf(str, fmt, arg);
+	return str;
+}
+
+char *opt_set_bool(bool *b)
+{
+	*b = true;
+	return NULL;
+}
+
+char *opt_set_false(bool *b)
+{
+    *b = false;
+    return NULL;
+}
+
+char *opt_set_invbool(bool *b)
+{
+	*b = false;
+	return NULL;
+}
+
+char *opt_set_bool_arg(const char *arg, bool *b)
+{
+	if (!strcasecmp(arg, "yes") || !strcasecmp(arg, "true"))
+		return opt_set_bool(b);
+	if (!strcasecmp(arg, "no") || !strcasecmp(arg, "false"))
+		return opt_set_invbool(b);
+
+	return opt_invalid_argument(arg);
+}
+
+char *opt_set_invbool_arg(const char *arg, bool *b)
+{
+	char *err = opt_set_bool_arg(arg, b);
+
+	if (!err)
+		*b = !*b;
+	return err;
+}
+
+/* Set a char *. */
+char *opt_set_charp(const char *arg, char **p)
+{
+	*p = (char *)arg;
+	return NULL;
+}
+
+/* Set an integer value, various forms.  Sets to 1 on arg == NULL. */
+char *opt_set_intval(const char *arg, int *i)
+{
+	long l;
+	char *err = opt_set_longval(arg, &l);
+
+	if (err)
+		return err;
+	*i = l;
+	/* Beware truncation... */
+	if (*i != l)
+		return arg_bad("value '%s' does not fit into an integer", arg);
+	return err;
+}
+
+char *opt_set_floatval(const char *arg, float *f)
+{
+	char *endp;
+
+	errno = 0;
+	*f = strtof(arg, &endp);
+	if (*endp || !arg[0])
+		return arg_bad("'%s' is not a number", arg);
+	if (errno)
+		return arg_bad("'%s' is out of range", arg);
+	return NULL;
+}
+
+char *opt_set_uintval(const char *arg, unsigned int *ui)
+{
+	int i;
+	char *err = opt_set_intval(arg, &i);
+
+	if (err)
+		return err;
+	if (i < 0)
+		return arg_bad("'%s' is negative", arg);
+	*ui = i;
+	return NULL;
+}
+
+char *opt_set_longval(const char *arg, long *l)
+{
+	char *endp;
+
+	/* This is how the manpage says to do it.  Yech. */
+	errno = 0;
+	*l = strtol(arg, &endp, 0);
+	if (*endp || !arg[0])
+		return arg_bad("'%s' is not a number", arg);
+	if (errno)
+		return arg_bad("'%s' is out of range", arg);
+	return NULL;
+}
+
+char *opt_set_ulongval(const char *arg, unsigned long *ul)
+{
+	long int l;
+	char *err;
+	
+	err = opt_set_longval(arg, &l);
+	if (err)
+		return err;
+	*ul = l;
+	if (l < 0)
+		return arg_bad("'%s' is negative", arg);
+	return NULL;
+}
+
+char *opt_inc_intval(int *i)
+{
+	(*i)++;
+	return NULL;
+}
+
+/* Display version string. */
+char *opt_version_and_exit(const char *version)
+{
+	printf("%s\n", version);
+	fflush(stdout);
+	exit(0);
+}
+
+char *opt_usage_and_exit(const char *extra)
+{
+	printf("%s", opt_usage(opt_argv0, extra));
+	fflush(stdout);
+	exit(0);
+}
+
+void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b)
+{
+	strncpy(buf, *b ? "true" : "false", OPT_SHOW_LEN);
+}
+
+void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b)
+{
+	strncpy(buf, *b ? "false" : "true", OPT_SHOW_LEN);
+}
+
+void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p)
+{
+	size_t len = strlen(*p);
+	buf[0] = '"';
+	if (len > OPT_SHOW_LEN - 2)
+		len = OPT_SHOW_LEN - 2;
+	strncpy(buf+1, *p, len);
+	buf[1+len] = '"';
+	if (len < OPT_SHOW_LEN - 2)
+		buf[2+len] = '\0';
+}
+
+/* Set an integer value, various forms.  Sets to 1 on arg == NULL. */
+void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i)
+{
+	snprintf(buf, OPT_SHOW_LEN, "%i", *i);
+}
+
+void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f)
+{
+	snprintf(buf, OPT_SHOW_LEN, "%.1f", *f);
+}
+
+void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui)
+{
+	snprintf(buf, OPT_SHOW_LEN, "%u", *ui);
+}
+
+void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l)
+{
+	snprintf(buf, OPT_SHOW_LEN, "%li", *l);
+}
+
+void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul)
+{
+	snprintf(buf, OPT_SHOW_LEN, "%lu", *ul);
+}

+ 255 - 0
ccan/opt/opt.c

@@ -0,0 +1,255 @@
+#include <ccan/opt/opt.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef WIN32
+	#include <err.h>
+#else
+#include <libgen.h>
+	#define errx(status, fmt, ...) { \
+			fprintf(stderr, fmt, __VA_ARGS__); \
+			fprintf(stderr, "\n"); \
+			exit(status); }
+#endif
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include "private.h"
+
+struct opt_table *opt_table;
+unsigned int opt_count, opt_num_short, opt_num_short_arg, opt_num_long;
+const char *opt_argv0;
+
+/* Returns string after first '-'. */
+static const char *first_name(const char *names, unsigned *len)
+{
+	*len = strcspn(names + 1, "|= ");
+	return names + 1;
+}
+
+static const char *next_name(const char *names, unsigned *len)
+{
+	names += *len;
+	if (names[0] == ' ' || names[0] == '=' || names[0] == '\0')
+		return NULL;
+	return first_name(names + 1, len);
+}
+
+static const char *first_opt(unsigned *i, unsigned *len)
+{
+	for (*i = 0; *i < opt_count; (*i)++) {
+		if (opt_table[*i].type == OPT_SUBTABLE)
+			continue;
+		return first_name(opt_table[*i].names, len);
+	}
+	return NULL;
+}
+
+static const char *next_opt(const char *p, unsigned *i, unsigned *len)
+{
+	for (; *i < opt_count; (*i)++) {
+		if (opt_table[*i].type == OPT_SUBTABLE)
+			continue;
+		if (!p)
+			return first_name(opt_table[*i].names, len);
+		p = next_name(p, len);
+		if (p)
+			return p;
+	}
+	return NULL;
+}
+
+const char *first_lopt(unsigned *i, unsigned *len)
+{
+	const char *p;
+	for (p = first_opt(i, len); p; p = next_opt(p, i, len)) {
+		if (p[0] == '-') {
+			/* Skip leading "-" */
+			(*len)--;
+			p++;
+			break;
+		}
+	}
+	return p;
+}
+
+const char *next_lopt(const char *p, unsigned *i, unsigned *len)
+{
+	for (p = next_opt(p, i, len); p; p = next_opt(p, i, len)) {
+		if (p[0] == '-') {
+			/* Skip leading "-" */
+			(*len)--;
+			p++;
+			break;
+		}
+	}
+	return p;
+}
+
+const char *first_sopt(unsigned *i)
+{
+	const char *p;
+	unsigned int len = 0 /* GCC bogus warning */;
+
+	for (p = first_opt(i, &len); p; p = next_opt(p, i, &len)) {
+		if (p[0] != '-')
+			break;
+	}
+	return p;
+}
+
+const char *next_sopt(const char *p, unsigned *i)
+{
+	unsigned int len = 1;
+	for (p = next_opt(p, i, &len); p; p = next_opt(p, i, &len)) {
+		if (p[0] != '-')
+			break;
+	}
+	return p;
+}
+
+static void check_opt(const struct opt_table *entry)
+{
+	const char *p;
+	unsigned len;
+
+	if (entry->type != OPT_HASARG && entry->type != OPT_NOARG && entry->type != OPT_PROCESSARG)
+		errx(1, "Option %s: unknown entry type %u",
+		     entry->names, entry->type);
+
+	if (!entry->desc)
+		errx(1, "Option %s: description cannot be NULL", entry->names);
+
+
+	if (entry->names[0] != '-')
+		errx(1, "Option %s: does not begin with '-'", entry->names);
+
+	for (p = first_name(entry->names, &len); p; p = next_name(p, &len)) {
+		if (*p == '-') {
+			if (len == 1)
+				errx(1, "Option %s: invalid long option '--'",
+				     entry->names);
+			opt_num_long++;
+		} else {
+			if (len != 1)
+				errx(1, "Option %s: invalid short option"
+				     " '%.*s'", entry->names, len+1, p-1);
+			opt_num_short++;
+			if (entry->type == OPT_HASARG || entry->type == OPT_PROCESSARG)
+				opt_num_short_arg++;
+		}
+		/* Don't document args unless there are some. */
+		if (entry->type == OPT_NOARG) {
+			if (p[len] == ' ' || p[len] == '=')
+				errx(1, "Option %s: does not take arguments"
+				     " '%s'", entry->names, p+len+1);
+		}
+	}
+}
+
+static void add_opt(const struct opt_table *entry)
+{
+	opt_table = realloc(opt_table, sizeof(opt_table[0]) * (opt_count+1));
+	opt_table[opt_count++] = *entry;
+}
+
+void _opt_register(const char *names, enum opt_type type,
+		   char *(*cb)(void *arg),
+		   char *(*cb_arg)(const char *optarg, void *arg),
+		   void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
+		   const void *arg, const char *desc)
+{
+	struct opt_table opt;
+	opt.names = names;
+	opt.type = type;
+	opt.cb = cb;
+	opt.cb_arg = cb_arg;
+	opt.show = show;
+	opt.u.carg = arg;
+	opt.desc = desc;
+	check_opt(&opt);
+	add_opt(&opt);
+}
+
+void opt_register_table(const struct opt_table entry[], const char *desc)
+{
+	unsigned int i, start = opt_count;
+
+	if (desc) {
+		struct opt_table heading = OPT_SUBTABLE(NULL, desc);
+		add_opt(&heading);
+	}
+	for (i = 0; entry[i].type != OPT_END; i++) {
+		if (entry[i].type == OPT_SUBTABLE)
+			opt_register_table(subtable_of(&entry[i]),
+					   entry[i].desc);
+		else {
+			check_opt(&entry[i]);
+			add_opt(&entry[i]);
+		}
+	}
+	/* We store the table length in arg ptr. */
+	if (desc)
+		opt_table[start].u.tlen = (opt_count - start);
+}
+
+/* Parse your arguments. */
+bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
+{
+	int ret;
+	unsigned offset = 0;
+	
+	#ifdef WIN32
+	char *original_argv0 = argv[0];
+	argv[0] = (char*)basename(argv[0]);
+	#endif
+
+	/* This helps opt_usage. */
+	opt_argv0 = argv[0];
+
+	while ((ret = parse_one(argc, argv, &offset, errlog)) == 1);
+	
+	#ifdef WIN32
+	argv[0] = original_argv0;
+	#endif
+
+	/* parse_one returns 0 on finish, -1 on error */
+	return (ret == 0);
+}
+
+void opt_free_table(void)
+{
+	free(opt_table);
+	opt_table=0;
+}
+
+void opt_log_stderr(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+}
+
+void opt_log_stderr_exit(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+	exit(1);
+}
+
+char *opt_invalid_argument(const char *arg)
+{
+	char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg));
+	sprintf(str, "Invalid argument '%s'", arg);
+	return str;
+}

+ 367 - 0
ccan/opt/opt.h

@@ -0,0 +1,367 @@
+#ifndef CCAN_OPT_H
+#define CCAN_OPT_H
+#include <ccan/compiler/compiler.h>
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+struct opt_table;
+
+/**
+ * OPT_WITHOUT_ARG() - macro for initializing an opt_table entry (without arg)
+ * @names: the names of the option eg. "--foo", "-f" or "--foo|-f|--foobar".
+ * @cb: the callback when the option is found.
+ * @arg: the argument to hand to @cb.
+ * @desc: the description for opt_usage(), or opt_hidden.
+ *
+ * This is a typesafe wrapper for initializing a struct opt_table.  The callback
+ * of type "char *cb(type *)", "char *cb(const type *)" or "char *cb(void *)",
+ * where "type" is the type of the @arg argument.
+ *
+ * If the @cb returns non-NULL, opt_parse() will stop parsing, use the
+ * returned string to form an error message for errlog(), free() the
+ * string and return false.
+ *
+ * Any number of equivalent short or long options can be listed in @names,
+ * separated by '|'.  Short options are a single hyphen followed by a single
+ * character, long options are two hyphens followed by one or more characters.
+ *
+ * See Also:
+ *	OPT_WITH_ARG()
+ */
+#define OPT_WITHOUT_ARG(names, cb, arg, desc)	\
+	{ (names), OPT_CB_NOARG((cb), (arg)), { (arg) }, (desc) }
+
+/**
+ * OPT_WITH_ARG() - macro for initializing long and short option (with arg)
+ * @names: the option names eg. "--foo=<arg>", "-f" or "-f|--foo <arg>".
+ * @cb: the callback when the option is found (along with <arg>).
+ * @show: the callback to print the value in get_usage (or NULL)
+ * @arg: the argument to hand to @cb and @show
+ * @desc: the description for opt_usage(), or opt_hidden.
+ *
+ * This is a typesafe wrapper for initializing a struct opt_table.  The callback
+ * is of type "char *cb(const char *, type *)",
+ * "char *cb(const char *, const type *)" or "char *cb(const char *, void *)",
+ * where "type" is the type of the @arg argument.  The first argument to the
+ * @cb is the argument found on the commandline.
+ *
+ * Similarly, if @show is not NULL, it should be of type "void *show(char *,
+ * const type *)".  It should write up to OPT_SHOW_LEN bytes into the first
+ * argument; unless it uses the entire OPT_SHOW_LEN bytes it should
+ * nul-terminate that buffer.
+ *
+ * Any number of equivalent short or long options can be listed in @names,
+ * separated by '|'.  Short options are a single hyphen followed by a single
+ * character, long options are two hyphens followed by one or more characters.
+ * A space or equals in @names is ignored for parsing, and only used
+ * for printing the usage.
+ *
+ * If the @cb returns non-NULL, opt_parse() will stop parsing, use the
+ * returned string to form an error message for errlog(), free() the
+ * string and return false.
+ *
+ * See Also:
+ *	OPT_WITHOUT_ARG()
+ */
+#define OPT_WITH_ARG(name, cb, show, arg, desc)	\
+	{ (name), OPT_CB_ARG((cb), (show), (arg)), { (arg) }, (desc) }
+
+/**
+ * OPT_WITH_CBARG() - variant of OPT_WITH_ARG which assigns arguments to arg
+ * and then performs the callback function on the args as well.
+ */
+#define OPT_WITH_CBARG(name, cb, show, arg, desc)	\
+	{ (name), OPT_CB_WITHARG((cb), (show), (arg)), { (arg) }, (desc) }
+
+/**
+ * OPT_SUBTABLE() - macro for including another table inside a table.
+ * @table: the table to include in this table.
+ * @desc: description of this subtable (for opt_usage()) or NULL.
+ */
+#define OPT_SUBTABLE(table, desc)					\
+	{ (const char *)(table), OPT_SUBTABLE,				\
+	  sizeof(_check_is_entry(table)) ? NULL : NULL, NULL, NULL,	\
+	  { NULL }, (desc) }
+
+/**
+ * OPT_ENDTABLE - macro to create final entry in table.
+ *
+ * This must be the final element in the opt_table array.
+ */
+#define OPT_ENDTABLE { NULL, OPT_END, NULL, NULL, NULL, { NULL }, NULL }
+
+/**
+ * opt_register_table - register a table of options
+ * @table: the table of options
+ * @desc: description of this subtable (for opt_usage()) or NULL.
+ *
+ * The table must be terminated by OPT_ENDTABLE.
+ *
+ * Example:
+ * static int verbose = 0;
+ * static struct opt_table opts[] = {
+ * 	OPT_WITHOUT_ARG("--verbose", opt_inc_intval, &verbose,
+ *			"Verbose mode (can be specified more than once)"),
+ * 	OPT_WITHOUT_ARG("-v", opt_inc_intval, &verbose,
+ *			"Verbose mode (can be specified more than once)"),
+ * 	OPT_WITHOUT_ARG("--usage", opt_usage_and_exit,
+ * 			"args...\nA silly test program.",
+ *			"Print this message."),
+ * 	OPT_ENDTABLE
+ * };
+ *
+ * ...
+ *	opt_register_table(opts, NULL);
+ */
+void opt_register_table(const struct opt_table *table, const char *desc);
+
+/**
+ * opt_register_noarg - register an option with no arguments
+ * @names: the names of the option eg. "--foo", "-f" or "--foo|-f|--foobar".
+ * @cb: the callback when the option is found.
+ * @arg: the argument to hand to @cb.
+ * @desc: the verbose description of the option (for opt_usage()), or NULL.
+ *
+ * This is used for registering a single commandline option which takes
+ * no argument.
+ *
+ * The callback is of type "char *cb(type *)", "char *cb(const type *)"
+ * or "char *cb(void *)", where "type" is the type of the @arg
+ * argument.
+ *
+ * If the @cb returns non-NULL, opt_parse() will stop parsing, use the
+ * returned string to form an error message for errlog(), free() the
+ * string and return false.
+ */
+#define opt_register_noarg(names, cb, arg, desc)			\
+	_opt_register((names), OPT_CB_NOARG((cb), (arg)), (arg), (desc))
+
+/**
+ * opt_register_arg - register an option with an arguments
+ * @names: the names of the option eg. "--foo", "-f" or "--foo|-f|--foobar".
+ * @cb: the callback when the option is found.
+ * @show: the callback to print the value in get_usage (or NULL)
+ * @arg: the argument to hand to @cb.
+ * @desc: the verbose description of the option (for opt_usage()), or NULL.
+ *
+ * This is used for registering a single commandline option which takes
+ * an argument.
+ *
+ * The callback is of type "char *cb(const char *, type *)",
+ * "char *cb(const char *, const type *)" or "char *cb(const char *, void *)",
+ * where "type" is the type of the @arg argument.  The first argument to the
+ * @cb is the argument found on the commandline.
+ *
+ * At least one of @longopt and @shortopt must be non-zero.  If the
+ * @cb returns false, opt_parse() will stop parsing and return false.
+ *
+ * Example:
+ * static char *explode(const char *optarg, void *unused)
+ * {
+ *	errx(1, "BOOM! %s", optarg);
+ * }
+ * ...
+ *	opt_register_arg("--explode|--boom", explode, NULL, NULL, opt_hidden);
+ */
+#define opt_register_arg(names, cb, show, arg, desc)			\
+	_opt_register((names), OPT_CB_ARG((cb), (show), (arg)), (arg), (desc))
+
+/**
+ * opt_parse - parse arguments.
+ * @argc: pointer to argc
+ * @argv: argv array.
+ * @errlog: the function to print errors
+ *
+ * This iterates through the command line and calls callbacks registered with
+ * opt_register_table()/opt_register_arg()/opt_register_noarg().  If there
+ * are unknown options, missing arguments or a callback returns false, then
+ * an error message is printed and false is returned.
+ *
+ * On success, argc and argv are adjusted so only the non-option elements
+ * remain, and true is returned.
+ *
+ * Example:
+ *	if (!opt_parse(&argc, argv, opt_log_stderr)) {
+ *		printf("You screwed up, aborting!\n");
+ *		exit(1);
+ *	}
+ *
+ * See Also:
+ *	opt_log_stderr, opt_log_stderr_exit
+ */
+bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...));
+
+/**
+ * opt_free_table - free the table.
+ *
+ * This frees the internal memory. Call this as the last
+ * opt function.
+ */
+void opt_free_table(void);
+
+/**
+ * opt_log_stderr - print message to stderr.
+ * @fmt: printf-style format.
+ *
+ * This is a helper for opt_parse, to print errors to stderr.
+ *
+ * See Also:
+ *	opt_log_stderr_exit
+ */
+void opt_log_stderr(const char *fmt, ...);
+
+/**
+ * opt_log_stderr_exit - print message to stderr, then exit(1)
+ * @fmt: printf-style format.
+ *
+ * Just like opt_log_stderr, only then does exit(1).  This means that
+ * when handed to opt_parse, opt_parse will never return false.
+ *
+ * Example:
+ *	// This never returns false; just exits if there's an erorr.
+ *	opt_parse(&argc, argv, opt_log_stderr_exit);
+ */
+void opt_log_stderr_exit(const char *fmt, ...);
+
+/**
+ * opt_invalid_argument - helper to allocate an "Invalid argument '%s'" string
+ * @arg: the argument which was invalid.
+ *
+ * This is a helper for callbacks to return a simple error string.
+ */
+char *opt_invalid_argument(const char *arg);
+
+/**
+ * opt_usage - create usage message
+ * @argv0: the program name
+ * @extra: extra details to print after the initial command, or NULL.
+ *
+ * Creates a usage message, with the program name, arguments, some extra details
+ * and a table of all the options with their descriptions.  If an option has
+ * description opt_hidden, it is not shown here.
+ *
+ * If "extra" is NULL, then the extra information is taken from any
+ * registered option which calls opt_usage_and_exit().  This avoids duplicating
+ * that string in the common case.
+ *
+ * The result should be passed to free().
+ */
+char *opt_usage(const char *argv0, const char *extra);
+
+/**
+ * opt_hidden - string for undocumented options.
+ *
+ * This can be used as the desc parameter if you want an option not to be
+ * shown by opt_usage().
+ */
+extern const char opt_hidden[];
+
+/* Maximum length of arg to show in opt_usage */
+#define OPT_SHOW_LEN 80
+
+/* Standard helpers.  You can write your own: */
+/* Sets the @b to true. */
+char *opt_set_bool(bool *b);
+/* Sets the @b to false. */
+char *opt_set_false(bool *b);
+/* Sets @b based on arg: (yes/no/true/false). */
+char *opt_set_bool_arg(const char *arg, bool *b);
+void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b);
+/* The inverse */
+char *opt_set_invbool(bool *b);
+void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b);
+/* Sets @b based on !arg: (yes/no/true/false). */
+char *opt_set_invbool_arg(const char *arg, bool *b);
+
+/* Set a char *. */
+char *opt_set_charp(const char *arg, char **p);
+void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p);
+
+/* Set an integer value, various forms.  Sets to 1 on arg == NULL. */
+char *opt_set_intval(const char *arg, int *i);
+void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i);
+char *opt_set_floatval(const char *arg, float *f);
+void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f);
+char *opt_set_uintval(const char *arg, unsigned int *ui);
+void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui);
+char *opt_set_longval(const char *arg, long *l);
+void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l);
+char *opt_set_ulongval(const char *arg, unsigned long *ul);
+void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul);
+
+/* Increment. */
+char *opt_inc_intval(int *i);
+
+/* Display version string to stdout, exit(0). */
+char *opt_version_and_exit(const char *version);
+
+/* Display usage string to stdout, exit(0). */
+char *opt_usage_and_exit(const char *extra);
+
+/* Below here are private declarations. */
+/* You can use this directly to build tables, but the macros will ensure
+ * consistency and type safety. */
+enum opt_type {
+	OPT_NOARG = 1,		/* -f|--foo */
+	OPT_HASARG = 2,		/* -f arg|--foo=arg|--foo arg */
+	OPT_PROCESSARG = 4,
+	OPT_SUBTABLE = 8,	/* Actually, longopt points to a subtable... */
+	OPT_END = 16,		/* End of the table. */
+};
+
+struct opt_table {
+	const char *names; /* pipe-separated names, --longopt or -s */
+	enum opt_type type;
+	char *(*cb)(void *arg); /* OPT_NOARG */
+	char *(*cb_arg)(const char *optarg, void *arg); /* OPT_HASARG */
+	void (*show)(char buf[OPT_SHOW_LEN], const void *arg);
+	union {
+		const void *carg;
+		void *arg;
+		size_t tlen;
+	} u;
+	const char *desc;
+};
+
+/* Resolves to the four parameters for non-arg callbacks. */
+#define OPT_CB_NOARG(cb, arg)				\
+	OPT_NOARG,					\
+	typesafe_cb_cast3(char *(*)(void *),	\
+			  char *(*)(typeof(*(arg))*),	\
+			  char *(*)(const typeof(*(arg))*),	\
+			  char *(*)(const void *), (cb)),	\
+	NULL, NULL
+
+/* Resolves to the four parameters for arg callbacks. */
+#define OPT_CB_ARG(cb, show, arg)					\
+	OPT_HASARG, NULL,						\
+	typesafe_cb_cast3(char *(*)(const char *,void *),	\
+			  char *(*)(const char *, typeof(*(arg))*),	\
+			  char *(*)(const char *, const typeof(*(arg))*), \
+			  char *(*)(const char *, const void *),	\
+			  (cb)),					\
+	typesafe_cb_cast(void (*)(char buf[], const void *),		\
+			 void (*)(char buf[], const typeof(*(arg))*), (show))
+
+#define OPT_CB_WITHARG(cb, show, arg)					\
+	OPT_PROCESSARG, NULL,						\
+	typesafe_cb_cast3(char *(*)(const char *,void *),	\
+			  char *(*)(const char *, typeof(*(arg))*),	\
+			  char *(*)(const char *, const typeof(*(arg))*), \
+			  char *(*)(const char *, const void *),	\
+			  (cb)),					\
+	typesafe_cb_cast(void (*)(char buf[], const void *),		\
+			 void (*)(char buf[], const typeof(*(arg))*), (show))
+
+/* Non-typesafe register function. */
+void _opt_register(const char *names, enum opt_type type,
+		   char *(*cb)(void *arg),
+		   char *(*cb_arg)(const char *optarg, void *arg),
+		   void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
+		   const void *arg, const char *desc);
+
+/* We use this to get typechecking for OPT_SUBTABLE */
+static inline int _check_is_entry(struct opt_table *e UNUSED) { return 0; }
+
+#endif /* CCAN_OPT_H */

+ 132 - 0
ccan/opt/parse.c

@@ -0,0 +1,132 @@
+/* Actual code to parse commandline. */
+#include <ccan/opt/opt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "private.h"
+
+/* glibc does this as:
+/tmp/opt-example: invalid option -- 'x'
+/tmp/opt-example: unrecognized option '--long'
+/tmp/opt-example: option '--someflag' doesn't allow an argument
+/tmp/opt-example: option '--s' is ambiguous
+/tmp/opt-example: option requires an argument -- 's'
+*/
+static int parse_err(void (*errlog)(const char *fmt, ...),
+		     const char *argv0, const char *arg, unsigned len,
+		     const char *problem)
+{
+	errlog("%s: %.*s: %s", argv0, len, arg, problem);
+	return -1;
+}
+
+static void consume_option(int *argc, char *argv[], unsigned optnum)
+{
+	memmove(&argv[optnum], &argv[optnum+1],
+		sizeof(argv[optnum]) * (*argc-optnum));
+	(*argc)--;
+}
+
+/* Returns 1 if argument consumed, 0 if all done, -1 on error. */
+int parse_one(int *argc, char *argv[], unsigned *offset,
+	      void (*errlog)(const char *fmt, ...))
+{
+	unsigned i, arg, len;
+	const char *o, *optarg = NULL;
+	char *problem;
+
+	if (getenv("POSIXLY_CORRECT")) {
+		/* Don't find options after non-options. */
+		arg = 1;
+	} else {
+		for (arg = 1; argv[arg]; arg++) {
+			if (argv[arg][0] == '-')
+				break;
+		}
+	}
+
+	if (!argv[arg] || argv[arg][0] != '-')
+		return 0;
+
+	/* Special arg terminator option. */
+	if (strcmp(argv[arg], "--") == 0) {
+		consume_option(argc, argv, arg);
+		return 0;
+	}
+
+	/* Long options start with -- */
+	if (argv[arg][1] == '-') {
+		assert(*offset == 0);
+		for (o = first_lopt(&i, &len); o; o = next_lopt(o, &i, &len)) {
+			if (strncmp(argv[arg] + 2, o, len) != 0)
+				continue;
+			if (argv[arg][2 + len] == '=')
+				optarg = argv[arg] + 2 + len + 1;
+			else if (argv[arg][2 + len] != '\0')
+				continue;
+			break;
+		}
+		if (!o)
+			return parse_err(errlog, argv[0],
+					 argv[arg], strlen(argv[arg]),
+					 "unrecognized option");
+		/* For error messages, we include the leading '--' */
+		o -= 2;
+		len += 2;
+	} else {
+		/* offset allows us to handle -abc */
+		for (o = first_sopt(&i); o; o = next_sopt(o, &i)) {
+			if (argv[arg][*offset + 1] != *o)
+				continue;
+			(*offset)++;
+			break;
+		}
+		if (!o)
+			return parse_err(errlog, argv[0],
+					 argv[arg], strlen(argv[arg]),
+					 "unrecognized option");
+		/* For error messages, we include the leading '-' */
+		o--;
+		len = 2;
+	}
+
+	if (opt_table[i].type == OPT_NOARG) {
+		if (optarg)
+			return parse_err(errlog, argv[0], o, len,
+					 "doesn't allow an argument");
+		problem = opt_table[i].cb(opt_table[i].u.arg);
+	} else {
+		if (!optarg) {
+			/* Swallow any short options as optarg, eg -afile */
+			if (*offset && argv[arg][*offset + 1]) {
+				optarg = argv[arg] + *offset + 1;
+				*offset = 0;
+			} else
+				optarg = argv[arg+1];
+		}
+		if (!optarg)
+			return parse_err(errlog, argv[0], o, len,
+					 "requires an argument");
+		if (opt_table[i].type == OPT_PROCESSARG)
+			opt_set_charp(optarg, opt_table[i].u.arg);
+		problem = opt_table[i].cb_arg(optarg, opt_table[i].u.arg);
+	}
+
+	if (problem) {
+		parse_err(errlog, argv[0], o, len, problem);
+		free(problem);
+		return -1;
+	}
+
+	/* If no more letters in that short opt, reset offset. */
+	if (*offset && !argv[arg][*offset + 1])
+		*offset = 0;
+
+	/* All finished with that option? */
+	if (*offset == 0) {
+		consume_option(argc, argv, arg);
+		if (optarg && optarg == argv[arg])
+			consume_option(argc, argv, arg);
+	}
+	return 1;
+}

+ 19 - 0
ccan/opt/private.h

@@ -0,0 +1,19 @@
+#ifndef CCAN_OPT_PRIVATE_H
+#define CCAN_OPT_PRIVATE_H
+
+extern struct opt_table *opt_table;
+extern unsigned int opt_count, opt_num_short, opt_num_short_arg, opt_num_long;
+
+extern const char *opt_argv0;
+
+#define subtable_of(entry) ((struct opt_table *)((entry)->names))
+
+const char *first_sopt(unsigned *i);
+const char *next_sopt(const char *names, unsigned *i);
+const char *first_lopt(unsigned *i, unsigned *len);
+const char *next_lopt(const char *p, unsigned *i, unsigned *len);
+
+int parse_one(int *argc, char *argv[], unsigned *offset,
+	      void (*errlog)(const char *fmt, ...));
+
+#endif /* CCAN_OPT_PRIVATE_H */

+ 13 - 0
ccan/opt/test/compile_ok-const-arg.c

@@ -0,0 +1,13 @@
+#include <ccan/opt/opt.h>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include <ccan/opt/usage.c>
+
+int main(int argc, char *argv[])
+{
+	opt_register_noarg("-v", opt_version_and_exit,
+			   (const char *)"1.2.3",
+			   (const char *)"Print version");
+	return 0;
+}

+ 144 - 0
ccan/opt/test/run-checkopt.c

@@ -0,0 +1,144 @@
+#include "config.h"
+#include <stdio.h>
+#include <ccan/tap/tap.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <err.h>
+#include "utils.h"
+
+/* We don't actually want it to exit... */
+static jmp_buf exited;
+#define errx save_and_jump
+
+static void save_and_jump(int ecode, const char *fmt, ...);
+
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/parse.c>
+
+static char *output = NULL;
+
+static int saved_vprintf(const char *fmt, va_list ap)
+{
+	char *p;
+	int ret = vasprintf(&p, fmt, ap);
+
+	if (output) {
+		output = realloc(output, strlen(output) + strlen(p) + 1);
+		strcat(output, p);
+		free(p);
+	} else
+		output = p;
+	return ret;
+}
+
+static void save_and_jump(int ecode, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	saved_vprintf(fmt, ap);
+	va_end(ap);
+	longjmp(exited, ecode + 1);
+}
+
+static void reset(void)
+{
+	free(output);
+	output = NULL;
+	free(opt_table);
+	opt_table = NULL;
+	opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
+}
+
+int main(int argc, char *argv[])
+{
+	int exitval;
+
+	plan_tests(14);
+
+	exitval = setjmp(exited);
+	if (exitval == 0) {
+		/* Bad type. */
+		_opt_register("-a", OPT_SUBTABLE, (void *)opt_version_and_exit,
+			      NULL, NULL, "1.2.3", "");
+		fail("_opt_register returned?");
+	} else {
+		ok1(exitval - 1 == 1);
+		ok1(strstr(output, "Option -a: unknown entry type"));
+	}
+	reset();
+
+	exitval = setjmp(exited);
+	if (exitval == 0) {
+		/* NULL description. */
+		opt_register_noarg("-a", test_noarg, "", NULL);
+		fail("_opt_register returned?");
+	} else {
+		ok1(exitval - 1 == 1);
+		ok1(strstr(output, "Option -a: description cannot be NULL"));
+	}
+	reset();
+
+	exitval = setjmp(exited);
+	if (exitval == 0) {
+		/* Bad option name. */
+		opt_register_noarg("a", test_noarg, "", "");
+		fail("_opt_register returned?");
+	} else {
+		ok1(exitval - 1 == 1);
+		ok1(strstr(output, "Option a: does not begin with '-'"));
+	}
+
+	reset();
+
+	exitval = setjmp(exited);
+	if (exitval == 0) {
+		/* Bad option name. */
+		opt_register_noarg("--", test_noarg, "", "");
+		fail("_opt_register returned?");
+	} else {
+		ok1(exitval - 1 == 1);
+		ok1(strstr(output, "Option --: invalid long option '--'"));
+	}
+
+	reset();
+
+	exitval = setjmp(exited);
+	if (exitval == 0) {
+		/* Bad option name. */
+		opt_register_noarg("--a|-aaa", test_noarg, "", "");
+		fail("_opt_register returned?");
+	} else {
+		ok1(exitval - 1 == 1);
+		ok1(strstr(output,
+			   "Option --a|-aaa: invalid short option '-aaa'"));
+	}
+	reset();
+
+	exitval = setjmp(exited);
+	if (exitval == 0) {
+		/* Documentation for non-optios. */
+		opt_register_noarg("--a foo", test_noarg, "", "");
+		fail("_opt_register returned?");
+	} else {
+		ok1(exitval - 1 == 1);
+		ok1(strstr(output,
+			   "Option --a foo: does not take arguments 'foo'"));
+	}
+	reset();
+
+	exitval = setjmp(exited);
+	if (exitval == 0) {
+		/* Documentation for non-optios. */
+		opt_register_noarg("--a=foo", test_noarg, "", "");
+		fail("_opt_register returned?");
+	} else {
+		ok1(exitval - 1 == 1);
+		ok1(strstr(output,
+			   "Option --a=foo: does not take arguments 'foo'"));
+	}
+	return exit_status();
+}

+ 49 - 0
ccan/opt/test/run-correct-reporting.c

@@ -0,0 +1,49 @@
+/* Make sure when multiple equivalent options, correct one is used for errors */
+
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include "utils.h"
+
+int main(int argc, char *argv[])
+{
+	plan_tests(12);
+
+	/* --aaa without args. */
+	opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", "");
+	ok1(!parse_args(&argc, &argv, "--aaa", NULL));
+	ok1(strstr(err_output, ": --aaa: requires an argument"));
+	free(err_output);
+	err_output = NULL;
+	ok1(!parse_args(&argc, &argv, "-a", NULL));
+	ok1(strstr(err_output, ": -a: requires an argument"));
+	free(err_output);
+	err_output = NULL;
+
+	/* Multiple */
+	opt_register_arg("--bbb|-b|-c|--ccc", test_arg, NULL, "aaa", "");
+	ok1(!parse_args(&argc, &argv, "--bbb", NULL));
+	ok1(strstr(err_output, ": --bbb: requires an argument"));
+	free(err_output);
+	err_output = NULL;
+	ok1(!parse_args(&argc, &argv, "-b", NULL));
+	ok1(strstr(err_output, ": -b: requires an argument"));
+	free(err_output);
+	err_output = NULL;
+	ok1(!parse_args(&argc, &argv, "-c", NULL));
+	ok1(strstr(err_output, ": -c: requires an argument"));
+	free(err_output);
+	err_output = NULL;
+	ok1(!parse_args(&argc, &argv, "--ccc", NULL));
+	ok1(strstr(err_output, ": --ccc: requires an argument"));
+	free(err_output);
+	err_output = NULL;
+
+	/* parse_args allocates argv */
+	free(argv);
+	return exit_status();
+}
+

+ 440 - 0
ccan/opt/test/run-helpers.c

@@ -0,0 +1,440 @@
+#include "config.h"
+#include <stdio.h>
+#include <ccan/tap/tap.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <limits.h>
+#include "utils.h"
+
+/* We don't actually want it to exit... */
+static jmp_buf exited;
+#define exit(status) longjmp(exited, (status) + 1)
+
+#define printf saved_printf
+static int saved_printf(const char *fmt, ...);
+
+#define fprintf saved_fprintf
+static int saved_fprintf(FILE *ignored, const char *fmt, ...);
+
+#define vfprintf(f, fmt, ap) saved_vprintf(fmt, ap)
+static int saved_vprintf(const char *fmt, va_list ap);
+
+#define malloc(size) saved_malloc(size)
+static void *saved_malloc(size_t size);
+
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/parse.c>
+
+static void reset_options(void)
+{
+	free(opt_table);
+	opt_table = NULL;
+	opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
+}
+
+static char *output = NULL;
+
+static int saved_vprintf(const char *fmt, va_list ap)
+{
+	char *p;
+	int ret = vasprintf(&p, fmt, ap);
+
+	if (output) {
+		output = realloc(output, strlen(output) + strlen(p) + 1);
+		strcat(output, p);
+		free(p);
+	} else
+		output = p;
+	return ret;
+}
+
+static int saved_printf(const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = saved_vprintf(fmt, ap);
+	va_end(ap);
+	return ret;
+}	
+
+static int saved_fprintf(FILE *ignored, const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = saved_vprintf(fmt, ap);
+	va_end(ap);
+	return ret;
+}	
+
+#undef malloc
+static void *last_allocation;
+static void *saved_malloc(size_t size)
+{
+	return last_allocation = malloc(size);
+}
+
+/* Test helpers. */
+int main(int argc, char *argv[])
+{
+	plan_tests(100);
+
+	/* opt_set_bool */
+	{
+		bool arg = false;
+		reset_options();
+		opt_register_noarg("-a", opt_set_bool, &arg, "");
+		ok1(parse_args(&argc, &argv, "-a", NULL));
+		ok1(arg);
+		opt_register_arg("-b", opt_set_bool_arg, NULL, &arg, "");
+		ok1(parse_args(&argc, &argv, "-b", "no", NULL));
+		ok1(!arg);
+		ok1(parse_args(&argc, &argv, "-b", "yes", NULL));
+		ok1(arg);
+		ok1(parse_args(&argc, &argv, "-b", "false", NULL));
+		ok1(!arg);
+		ok1(parse_args(&argc, &argv, "-b", "true", NULL));
+		ok1(arg);
+		ok1(!parse_args(&argc, &argv, "-b", "unknown", NULL));
+		ok1(arg);
+		ok1(strstr(err_output, ": -b: Invalid argument 'unknown'"));
+	}
+	/* opt_set_invbool */
+	{
+		bool arg = true;
+		reset_options();
+		opt_register_noarg("-a", opt_set_invbool, &arg, "");
+		ok1(parse_args(&argc, &argv, "-a", NULL));
+		ok1(!arg);
+		opt_register_arg("-b", opt_set_invbool_arg, NULL,
+				 &arg, "");
+		ok1(parse_args(&argc, &argv, "-b", "no", NULL));
+		ok1(arg);
+		ok1(parse_args(&argc, &argv, "-b", "yes", NULL));
+		ok1(!arg);
+		ok1(parse_args(&argc, &argv, "-b", "false", NULL));
+		ok1(arg);
+		ok1(parse_args(&argc, &argv, "-b", "true", NULL));
+		ok1(!arg);
+		ok1(!parse_args(&argc, &argv, "-b", "unknown", NULL));
+		ok1(!arg);
+		ok1(strstr(err_output, ": -b: Invalid argument 'unknown'"));
+	}
+	/* opt_set_charp */
+	{
+		char *arg = (char *)"wrong";
+		reset_options();
+		opt_register_arg("-a", opt_set_charp, NULL, &arg, "All");
+		ok1(parse_args(&argc, &argv, "-a", "string", NULL));
+		ok1(strcmp(arg, "string") == 0);
+	}
+	/* opt_set_intval */
+	{
+		int arg = 1000;
+		reset_options();
+		opt_register_arg("-a", opt_set_intval, NULL, &arg, "All");
+		ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+		ok1(arg == 9999);
+		ok1(parse_args(&argc, &argv, "-a", "-9999", NULL));
+		ok1(arg == -9999);
+		ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+		ok1(arg == 0);
+		ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL));
+		if (sizeof(int) == 4)
+			ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL));
+		else
+			fail("Handle other int sizes");
+	}
+	/* opt_set_uintval */
+	{
+		unsigned int arg = 1000;
+		reset_options();
+		opt_register_arg("-a", opt_set_uintval, NULL, &arg, "All");
+		ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+		ok1(arg == 9999);
+		ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL));
+		ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+		ok1(arg == 0);
+		ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL));
+		ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL));
+		if (ULONG_MAX == UINT_MAX) {
+			pass("Can't test overflow");
+			pass("Can't test error message");
+		} else {
+			char buf[30];
+			sprintf(buf, "%lu", ULONG_MAX);
+			ok1(!parse_args(&argc, &argv, "-a", buf, NULL));
+			ok1(strstr(err_output, ": -a: value '")
+			    && strstr(err_output, buf)
+			    && strstr(err_output, "' does not fit into an integer"));
+		}
+	}
+	/* opt_set_longval */
+	{
+		long int arg = 1000;
+		reset_options();
+		opt_register_arg("-a", opt_set_longval, NULL, &arg, "All");
+		ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+		ok1(arg == 9999);
+		ok1(parse_args(&argc, &argv, "-a", "-9999", NULL));
+		ok1(arg == -9999);
+		ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+		ok1(arg == 0);
+		ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL));
+		if (sizeof(long) == 4)
+			ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL));
+		else if (sizeof(long)== 8)
+			ok1(!parse_args(&argc, &argv, "-a", "18446744073709551616", NULL));
+		else
+			fail("FIXME: Handle other long sizes");
+	}
+	/* opt_set_ulongval */
+	{
+		unsigned long int arg = 1000;
+		reset_options();
+		opt_register_arg("-a", opt_set_ulongval, NULL, &arg, "All");
+		ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+		ok1(arg == 9999);
+		ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL));
+		ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+		ok1(arg == 0);
+		ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL));
+		if (sizeof(long) == 4)
+			ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL));
+		else if (sizeof(long)== 8)
+			ok1(!parse_args(&argc, &argv, "-a", "18446744073709551616", NULL));
+		else
+			fail("FIXME: Handle other long sizes");
+	}
+	/* opt_inc_intval */
+	{
+		int arg = 1000;
+		reset_options();
+		opt_register_noarg("-a", opt_inc_intval, &arg, "");
+		ok1(parse_args(&argc, &argv, "-a", NULL));
+		ok1(arg == 1001);
+		ok1(parse_args(&argc, &argv, "-a", "-a", NULL));
+		ok1(arg == 1003);
+		ok1(parse_args(&argc, &argv, "-aa", NULL));
+		ok1(arg == 1005);
+	}
+
+	/* opt_show_version_and_exit. */
+	{
+		int exitval;
+		reset_options();
+		opt_register_noarg("-a",
+				   opt_version_and_exit, "1.2.3", "");
+		/* parse_args allocates argv */
+		free(argv);
+
+		argc = 2;
+		argv = malloc(sizeof(argv[0]) * 3);
+		argv[0] = "thisprog";
+		argv[1] = "-a";
+		argv[2] = NULL;
+
+		exitval = setjmp(exited);
+		if (exitval == 0) {
+			opt_parse(&argc, argv, save_err_output);
+			fail("opt_show_version_and_exit returned?");
+		} else {
+			ok1(exitval - 1 == 0);
+		}
+		ok1(strcmp(output, "1.2.3\n") == 0);
+		free(output);
+		free(argv);
+		output = NULL;
+	}
+
+	/* opt_usage_and_exit. */
+	{
+		int exitval;
+		reset_options();
+		opt_register_noarg("-a",
+				   opt_usage_and_exit, "[args]", "");
+
+		argc = 2;
+		argv = malloc(sizeof(argv[0]) * 3);
+		argv[0] = "thisprog";
+		argv[1] = "-a";
+		argv[2] = NULL;
+
+		exitval = setjmp(exited);
+		if (exitval == 0) {
+			opt_parse(&argc, argv, save_err_output);
+			fail("opt_usage_and_exit returned?");
+		} else {
+			ok1(exitval - 1 == 0);
+		}
+		ok1(strstr(output, "[args]"));
+		ok1(strstr(output, argv[0]));
+		ok1(strstr(output, "[-a]"));
+		free(output);
+		free(argv);
+		/* It exits without freeing usage string. */
+		free(last_allocation);
+		output = NULL;
+	}
+
+	/* opt_show_bool */
+	{
+		bool b;
+		char buf[OPT_SHOW_LEN+2] = { 0 };
+		buf[OPT_SHOW_LEN] = '!';
+
+		b = true;
+		opt_show_bool(buf, &b);
+		ok1(strcmp(buf, "true") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+
+		b = false;
+		opt_show_bool(buf, &b);
+		ok1(strcmp(buf, "false") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+	}
+
+	/* opt_show_invbool */
+	{
+		bool b;
+		char buf[OPT_SHOW_LEN+2] = { 0 };
+		buf[OPT_SHOW_LEN] = '!';
+
+		b = true;
+		opt_show_invbool(buf, &b);
+		ok1(strcmp(buf, "false") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+
+		b = false;
+		opt_show_invbool(buf, &b);
+		ok1(strcmp(buf, "true") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+	}
+
+	/* opt_show_charp */
+	{
+		char str[OPT_SHOW_LEN*2], *p;
+		char buf[OPT_SHOW_LEN+2] = { 0 };
+		buf[OPT_SHOW_LEN] = '!';
+
+		/* Short test. */
+		p = str;
+		strcpy(p, "short");
+		opt_show_charp(buf, &p);
+		ok1(strcmp(buf, "\"short\"") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+
+		/* Truncate test. */
+		memset(p, 'x', OPT_SHOW_LEN*2);
+		p[OPT_SHOW_LEN*2-1] = '\0';
+		opt_show_charp(buf, &p);
+		ok1(buf[0] == '"');
+		ok1(buf[OPT_SHOW_LEN-1] == '"');
+		ok1(buf[OPT_SHOW_LEN] == '!');
+		ok1(strspn(buf+1, "x") == OPT_SHOW_LEN-2);
+	}
+
+	/* opt_show_intval */
+	{
+		int i;
+		char buf[OPT_SHOW_LEN+2] = { 0 };
+		buf[OPT_SHOW_LEN] = '!';
+
+		i = -77;
+		opt_show_intval(buf, &i);
+		ok1(strcmp(buf, "-77") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+
+		i = 77;
+		opt_show_intval(buf, &i);
+		ok1(strcmp(buf, "77") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+	}
+
+	/* opt_show_uintval */
+	{
+		unsigned int ui;
+		char buf[OPT_SHOW_LEN+2] = { 0 };
+		buf[OPT_SHOW_LEN] = '!';
+
+		ui = 4294967295U;
+		opt_show_uintval(buf, &ui);
+		ok1(strcmp(buf, "4294967295") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+	}
+
+	/* opt_show_longval */
+	{
+		long l;
+		char buf[OPT_SHOW_LEN+2] = { 0 };
+		buf[OPT_SHOW_LEN] = '!';
+
+		l = 1234567890L;
+		opt_show_longval(buf, &l);
+		ok1(strcmp(buf, "1234567890") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+	}
+
+	/* opt_show_ulongval */
+	{
+		unsigned long ul;
+		char buf[OPT_SHOW_LEN+2] = { 0 };
+		buf[OPT_SHOW_LEN] = '!';
+
+		ul = 4294967295UL;
+		opt_show_ulongval(buf, &ul);
+		ok1(strcmp(buf, "4294967295") == 0);
+		ok1(buf[OPT_SHOW_LEN] == '!');
+	}
+
+	/* opt_log_stderr. */
+	{
+		reset_options();
+		opt_register_noarg("-a",
+				   opt_usage_and_exit, "[args]", "");
+
+		argc = 2;
+		argv = malloc(sizeof(argv[0]) * 3);
+		argv[0] = "thisprog";
+		argv[1] = "--garbage";
+		argv[2] = NULL;
+		ok1(!opt_parse(&argc, argv, opt_log_stderr));
+		ok1(!strcmp(output,
+			    "thisprog: --garbage: unrecognized option\n"));
+		free(output);
+		free(argv);
+		output = NULL;
+	}
+
+	/* opt_log_stderr_exit. */
+	{
+		int exitval;
+		reset_options();
+		opt_register_noarg("-a",
+				   opt_usage_and_exit, "[args]", "");
+		argc = 2;
+		argv = malloc(sizeof(argv[0]) * 3);
+		argv[0] = "thisprog";
+		argv[1] = "--garbage";
+		argv[2] = NULL;
+		exitval = setjmp(exited);
+		if (exitval == 0) {
+			opt_parse(&argc, argv, opt_log_stderr_exit);
+			fail("opt_log_stderr_exit returned?");
+		} else {
+			ok1(exitval - 1 == 1);
+		}
+		free(argv);
+		ok1(!strcmp(output,
+			    "thisprog: --garbage: unrecognized option\n"));
+		free(output);
+		output = NULL;
+	}
+
+	return exit_status();
+}

+ 88 - 0
ccan/opt/test/run-iter.c

@@ -0,0 +1,88 @@
+#include <ccan/tap/tap.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "utils.h"
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+
+static void reset_options(void)
+{
+	free(opt_table);
+	opt_table = NULL;
+	opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
+}
+
+/* Test iterators. */
+int main(int argc, char *argv[])
+{
+	unsigned j, i, len = 0;
+	const char *p;
+
+	plan_tests(37 * 2);
+	for (j = 0; j < 2; j ++) {
+		reset_options();
+		/* Giving subtable a title makes an extra entry! */
+		opt_register_table(subtables, j == 0 ? NULL : "subtable");
+
+		p = first_lopt(&i, &len);
+		ok1(i == j + 0);
+		ok1(len == 3);
+		ok1(strncmp(p, "jjj", len) == 0);
+		p = next_lopt(p, &i, &len);
+		ok1(i == j + 0);
+		ok1(len == 3);
+		ok1(strncmp(p, "lll", len) == 0);
+		p = next_lopt(p, &i, &len);
+		ok1(i == j + 1);
+		ok1(len == 3);
+		ok1(strncmp(p, "mmm", len) == 0);
+		p = next_lopt(p, &i, &len);
+		ok1(i == j + 5);
+		ok1(len == 3);
+		ok1(strncmp(p, "ddd", len) == 0);
+		p = next_lopt(p, &i, &len);
+		ok1(i == j + 6);
+		ok1(len == 3);
+		ok1(strncmp(p, "eee", len) == 0);
+		p = next_lopt(p, &i, &len);
+		ok1(i == j + 7);
+		ok1(len == 3);
+		ok1(strncmp(p, "ggg", len) == 0);
+		p = next_lopt(p, &i, &len);
+		ok1(i == j + 8);
+		ok1(len == 3);
+		ok1(strncmp(p, "hhh", len) == 0);
+		p = next_lopt(p, &i, &len);
+		ok1(!p);
+
+		p = first_sopt(&i);
+		ok1(i == j + 0);
+		ok1(*p == 'j');
+		p = next_sopt(p, &i);
+		ok1(i == j + 0);
+		ok1(*p == 'l');
+		p = next_sopt(p, &i);
+		ok1(i == j + 1);
+		ok1(*p == 'm');
+		p = next_sopt(p, &i);
+		ok1(i == j + 2);
+		ok1(*p == 'a');
+		p = next_sopt(p, &i);
+		ok1(i == j + 3);
+		ok1(*p == 'b');
+		p = next_sopt(p, &i);
+		ok1(i == j + 7);
+		ok1(*p == 'g');
+		p = next_sopt(p, &i);
+		ok1(i == j + 8);
+		ok1(*p == 'h');
+		p = next_sopt(p, &i);
+		ok1(!p);
+	}
+
+	return exit_status();
+}

+ 33 - 0
ccan/opt/test/run-no-options.c

@@ -0,0 +1,33 @@
+/* Make sure we still work with no options registered */
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include "utils.h"
+
+int main(int argc, char *argv[])
+{
+	const char *myname = argv[0];
+
+	plan_tests(7);
+
+	/* Simple short arg.*/
+	ok1(!parse_args(&argc, &argv, "-a", NULL));
+	/* Simple long arg.*/
+	ok1(!parse_args(&argc, &argv, "--aaa", NULL));
+
+	/* Extra arguments preserved. */
+	ok1(parse_args(&argc, &argv, "extra", "args", NULL));
+	ok1(argc == 3);
+	ok1(argv[0] == myname);
+	ok1(strcmp(argv[1], "extra") == 0);
+	ok1(strcmp(argv[2], "args") == 0);
+
+	/* parse_args allocates argv */
+	free(argv);
+
+	return exit_status();
+}
+

+ 112 - 0
ccan/opt/test/run-usage.c

@@ -0,0 +1,112 @@
+#include <ccan/tap/tap.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "utils.h"
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+
+static char *my_cb(void *p)
+{
+	return NULL;
+}
+
+static void reset_options(void)
+{
+	free(opt_table);
+	opt_table = NULL;
+	opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
+}
+
+/* Test helpers. */
+int main(int argc, char *argv[])
+{
+	char *output;
+	char *longname = strdup("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+	char *shortname = strdup("shortname");
+
+	plan_tests(48);
+	opt_register_table(subtables, NULL);
+	opt_register_noarg("--kkk|-k", my_cb, NULL, "magic kkk option");
+	opt_register_noarg("-?", opt_usage_and_exit, "<MyArgs>...",
+			   "This message");
+	opt_register_arg("--longname", opt_set_charp, opt_show_charp,
+			 &longname, "a really long option default");
+	opt_register_arg("--shortname", opt_set_charp, opt_show_charp,
+			 &shortname, "a short option default");
+	output = opt_usage("my name", "ExTrA Args");
+	diag("%s", output);
+	ok1(strstr(output, "Usage: my name"));
+	ok1(strstr(output, "--jjj|-j|--lll|-l <arg>"));
+	ok1(strstr(output, "ExTrA Args"));
+	ok1(strstr(output, "-a "));
+	ok1(strstr(output, " Description of a\n"));
+	ok1(strstr(output, "-b <arg>"));
+	ok1(strstr(output, " Description of b (default: b)\n"));
+	ok1(strstr(output, "--ddd "));
+	ok1(strstr(output, " Description of ddd\n"));
+	ok1(strstr(output, "--eee <filename> "));
+	ok1(strstr(output, " (default: eee)\n"));
+	ok1(strstr(output, "long table options:\n"));
+	ok1(strstr(output, "--ggg|-g "));
+	ok1(strstr(output, " Description of ggg\n"));
+	ok1(strstr(output, "-h|--hhh <arg>"));
+	ok1(strstr(output, " Description of hhh\n"));
+	ok1(strstr(output, "--kkk|-k"));
+	ok1(strstr(output, "magic kkk option"));
+	/* This entry is hidden. */
+	ok1(!strstr(output, "--mmm|-m"));
+	free(output);
+
+	/* NULL should use string from registered options. */
+	output = opt_usage("my name", NULL);
+	diag("%s", output);
+	ok1(strstr(output, "Usage: my name"));
+	ok1(strstr(output, "--jjj|-j|--lll|-l <arg>"));
+	ok1(strstr(output, "<MyArgs>..."));
+	ok1(strstr(output, "-a "));
+	ok1(strstr(output, " Description of a\n"));
+	ok1(strstr(output, "-b <arg>"));
+	ok1(strstr(output, " Description of b (default: b)\n"));
+	ok1(strstr(output, "--ddd "));
+	ok1(strstr(output, " Description of ddd\n"));
+	ok1(strstr(output, "--eee <filename> "));
+	ok1(strstr(output, " (default: eee)\n"));
+	ok1(strstr(output, "long table options:\n"));
+	ok1(strstr(output, "--ggg|-g "));
+	ok1(strstr(output, " Description of ggg\n"));
+	ok1(strstr(output, "-h|--hhh <arg>"));
+	ok1(strstr(output, " Description of hhh\n"));
+	ok1(strstr(output, "--kkk|-k"));
+	ok1(strstr(output, "magic kkk option"));
+	ok1(strstr(output, "--longname"));
+	ok1(strstr(output, "a really long option default"));
+	ok1(strstr(output, "(default: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"...)"));
+	ok1(strstr(output, "--shortname"));
+	ok1(strstr(output, "a short option default"));
+	ok1(strstr(output, "(default: \"shortname\")"));
+	/* This entry is hidden. */
+	ok1(!strstr(output, "--mmm|-m"));
+	free(output);
+
+	reset_options();
+	/* Empty table test. */
+	output = opt_usage("nothing", NULL);
+	ok1(strstr(output, "Usage: nothing \n"));
+	free(output);
+
+	/* No short args. */
+	opt_register_noarg("--aaa", test_noarg, NULL, "AAAAll");
+	output = opt_usage("onearg", NULL);
+	ok1(strstr(output, "Usage: onearg \n"));
+	ok1(strstr(output, "--aaa"));
+	ok1(strstr(output, "AAAAll"));
+	free(output);
+
+	free(shortname);
+	free(longname);
+	return exit_status();
+}

+ 297 - 0
ccan/opt/test/run.c

@@ -0,0 +1,297 @@
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include "utils.h"
+
+static void reset_options(void)
+{
+	free(opt_table);
+	opt_table = NULL;
+	opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
+	free(err_output);
+	err_output = NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	const char *myname = argv[0];
+
+	plan_tests(215);
+
+	/* Simple short arg.*/
+	opt_register_noarg("-a", test_noarg, NULL, "All");
+	ok1(parse_args(&argc, &argv, "-a", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 1);
+
+	/* Simple long arg. */
+	opt_register_noarg("--aaa", test_noarg, NULL, "AAAAll");
+	ok1(parse_args(&argc, &argv, "--aaa", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 2);
+
+	/* Both long and short args. */
+	opt_register_noarg("--aaa|-a", test_noarg, NULL, "AAAAAAll");
+	ok1(parse_args(&argc, &argv, "--aaa", "-a", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 4);
+
+	/* Extra arguments preserved. */
+	ok1(parse_args(&argc, &argv, "--aaa", "-a", "extra", "args", NULL));
+	ok1(argc == 3);
+	ok1(argv[0] == myname);
+	ok1(strcmp(argv[1], "extra") == 0);
+	ok1(strcmp(argv[2], "args") == 0);
+	ok1(test_cb_called == 6);
+
+	/* Malformed versions. */
+	ok1(!parse_args(&argc, &argv, "--aaa=arg", NULL));
+	ok1(strstr(err_output, ": --aaa: doesn't allow an argument"));
+	ok1(!parse_args(&argc, &argv, "--aa", NULL));
+	ok1(strstr(err_output, ": --aa: unrecognized option"));
+	ok1(!parse_args(&argc, &argv, "--aaargh", NULL));
+	ok1(strstr(err_output, ": --aaargh: unrecognized option"));
+
+	/* Argument variants. */
+	reset_options();
+	test_cb_called = 0;
+	opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", "AAAAAAll");
+	ok1(parse_args(&argc, &argv, "--aaa", "aaa", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(test_cb_called == 1);
+
+	ok1(parse_args(&argc, &argv, "--aaa=aaa", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(test_cb_called == 2);
+
+	ok1(parse_args(&argc, &argv, "-a", "aaa", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(test_cb_called == 3);
+
+	/* Malformed versions. */
+	ok1(!parse_args(&argc, &argv, "-a", NULL));
+	ok1(strstr(err_output, ": -a: requires an argument"));
+	ok1(!parse_args(&argc, &argv, "--aaa", NULL));
+	ok1(strstr(err_output, ": --aaa: requires an argument"));
+	ok1(!parse_args(&argc, &argv, "--aa", NULL));
+	ok1(strstr(err_output, ": --aa: unrecognized option"));
+	ok1(!parse_args(&argc, &argv, "--aaargh", NULL));
+	ok1(strstr(err_output, ": --aaargh: unrecognized option"));
+
+	/* Now, tables. */
+	/* Short table: */
+	reset_options();
+	test_cb_called = 0;
+	opt_register_table(short_table, NULL);
+	ok1(parse_args(&argc, &argv, "-a", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 1);
+	/* This one needs an arg. */
+	ok1(parse_args(&argc, &argv, "-b", NULL) == false);
+	ok1(test_cb_called == 1);
+	ok1(parse_args(&argc, &argv, "-b", "b", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 2);
+
+	/* Long table: */
+	reset_options();
+	test_cb_called = 0;
+	opt_register_table(long_table, NULL);
+	ok1(parse_args(&argc, &argv, "--ddd", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 1);
+	/* This one needs an arg. */
+	ok1(parse_args(&argc, &argv, "--eee", NULL) == false);
+	ok1(test_cb_called == 1);
+	ok1(parse_args(&argc, &argv, "--eee", "eee", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 2);
+
+	/* Short and long, both. */
+	reset_options();
+	test_cb_called = 0;
+	opt_register_table(long_and_short_table, NULL);
+	ok1(parse_args(&argc, &argv, "-g", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 1);
+	ok1(parse_args(&argc, &argv, "--ggg", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 2);
+	/* This one needs an arg. */
+	ok1(parse_args(&argc, &argv, "-h", NULL) == false);
+	ok1(test_cb_called == 2);
+	ok1(parse_args(&argc, &argv, "-h", "hhh", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 3);
+	ok1(parse_args(&argc, &argv, "--hhh", NULL) == false);
+	ok1(test_cb_called == 3);
+	ok1(parse_args(&argc, &argv, "--hhh", "hhh", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 4);
+
+	/* Those will all work as tables. */
+	test_cb_called = 0;
+	reset_options();
+	opt_register_table(subtables, NULL);
+	ok1(parse_args(&argc, &argv, "-a", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 1);
+	/* This one needs an arg. */
+	ok1(parse_args(&argc, &argv, "-b", NULL) == false);
+	ok1(test_cb_called == 1);
+	ok1(parse_args(&argc, &argv, "-b", "b", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 2);
+
+	ok1(parse_args(&argc, &argv, "--ddd", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 3);
+	/* This one needs an arg. */
+	ok1(parse_args(&argc, &argv, "--eee", NULL) == false);
+	ok1(test_cb_called == 3);
+	ok1(parse_args(&argc, &argv, "--eee", "eee", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 4);
+
+	/* Short and long, both. */
+	ok1(parse_args(&argc, &argv, "-g", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 5);
+	ok1(parse_args(&argc, &argv, "--ggg", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 6);
+	/* This one needs an arg. */
+	ok1(parse_args(&argc, &argv, "-h", NULL) == false);
+	ok1(test_cb_called == 6);
+	ok1(parse_args(&argc, &argv, "-h", "hhh", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 7);
+	ok1(parse_args(&argc, &argv, "--hhh", NULL) == false);
+	ok1(test_cb_called == 7);
+	ok1(parse_args(&argc, &argv, "--hhh", "hhh", NULL));
+	ok1(argc == 1);
+	ok1(argv[0] == myname);
+	ok1(argv[1] == NULL);
+	ok1(test_cb_called == 8);
+
+	/* Now the tricky one: -? must not be confused with an unknown option */
+	test_cb_called = 0;
+	reset_options();
+
+	/* glibc's getopt does not handle ? with arguments. */
+	opt_register_noarg("-?", test_noarg, NULL, "Help");
+	ok1(parse_args(&argc, &argv, "-?", NULL));
+	ok1(test_cb_called == 1);
+	ok1(parse_args(&argc, &argv, "-a", NULL) == false);
+	ok1(test_cb_called == 1);
+	ok1(strstr(err_output, ": -a: unrecognized option"));
+	ok1(parse_args(&argc, &argv, "--aaaa", NULL) == false);
+	ok1(test_cb_called == 1);
+	ok1(strstr(err_output, ": --aaaa: unrecognized option"));
+
+	test_cb_called = 0;
+	reset_options();
+
+	/* Corner cases involving short arg parsing weirdness. */
+	opt_register_noarg("-a|--aaa", test_noarg, NULL, "a");
+	opt_register_arg("-b|--bbb", test_arg, NULL, "bbb", "b");
+	opt_register_arg("-c|--ccc", test_arg, NULL, "aaa", "c");
+	/* -aa == -a -a */
+	ok1(parse_args(&argc, &argv, "-aa", NULL));
+	ok1(test_cb_called == 2);
+	ok1(parse_args(&argc, &argv, "-aab", NULL) == false);
+	ok1(test_cb_called == 4);
+	ok1(strstr(err_output, ": -b: requires an argument"));
+	ok1(parse_args(&argc, &argv, "-bbbb", NULL));
+	ok1(test_cb_called == 5);
+	ok1(parse_args(&argc, &argv, "-aabbbb", NULL));
+	ok1(test_cb_called == 8);
+	ok1(parse_args(&argc, &argv, "-aabbbb", "-b", "bbb", NULL));
+	ok1(test_cb_called == 12);
+	ok1(parse_args(&argc, &argv, "-aabbbb", "--bbb", "bbb", NULL));
+	ok1(test_cb_called == 16);
+	ok1(parse_args(&argc, &argv, "-aabbbb", "--bbb=bbb", NULL));
+	ok1(test_cb_called == 20);
+	ok1(parse_args(&argc, &argv, "-aacaaa", NULL));
+	ok1(test_cb_called == 23);
+	ok1(parse_args(&argc, &argv, "-aacaaa", "-a", NULL));
+	ok1(test_cb_called == 27);
+	ok1(parse_args(&argc, &argv, "-aacaaa", "--bbb", "bbb", "-aacaaa",
+		       NULL));
+	ok1(test_cb_called == 34);
+
+	test_cb_called = 0;
+	reset_options();
+
+	/* -- and POSIXLY_CORRECT */
+	opt_register_noarg("-a|--aaa", test_noarg, NULL, "a");
+	ok1(parse_args(&argc, &argv, "-a", "--", "-a", NULL));
+	ok1(test_cb_called == 1);
+	ok1(argc == 2);
+	ok1(strcmp(argv[1], "-a") == 0);
+	ok1(!argv[2]);
+
+	unsetenv("POSIXLY_CORRECT");
+	ok1(parse_args(&argc, &argv, "-a", "somearg", "-a", "--", "-a", NULL));
+	ok1(test_cb_called == 3);
+	ok1(argc == 3);
+	ok1(strcmp(argv[1], "somearg") == 0);
+	ok1(strcmp(argv[2], "-a") == 0);
+	ok1(!argv[3]);
+
+	setenv("POSIXLY_CORRECT", "1", 1);
+	ok1(parse_args(&argc, &argv, "-a", "somearg", "-a", "--", "-a", NULL));
+	ok1(test_cb_called == 4);
+	ok1(argc == 5);
+	ok1(strcmp(argv[1], "somearg") == 0);
+	ok1(strcmp(argv[2], "-a") == 0);
+	ok1(strcmp(argv[3], "--") == 0);
+	ok1(strcmp(argv[4], "-a") == 0);
+	ok1(!argv[5]);
+
+	/* parse_args allocates argv */
+	free(argv);
+	return exit_status();
+}

+ 110 - 0
ccan/opt/test/utils.c

@@ -0,0 +1,110 @@
+#include "config.h"
+#include <ccan/tap/tap.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ccan/opt/opt.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdio.h>
+#include "utils.h"
+
+unsigned int test_cb_called;
+char *test_noarg(void *arg)
+{
+	test_cb_called++;
+	return NULL;
+}
+
+char *test_arg(const char *optarg, const char *arg)
+{
+	test_cb_called++;
+	ok1(strcmp(optarg, arg) == 0);
+	return NULL;
+}
+
+void show_arg(char buf[OPT_SHOW_LEN], const char *arg)
+{
+	strncpy(buf, arg, OPT_SHOW_LEN);
+}
+
+char *err_output = NULL;
+
+void save_err_output(const char *fmt, ...)
+{
+	va_list ap;
+	char *p;
+
+	va_start(ap, fmt);
+	/* Check return, for fascist gcc */
+	if (vasprintf(&p, fmt, ap) == -1)
+		p = NULL;
+	va_end(ap);
+
+	if (err_output) {
+		err_output = realloc(err_output,
+				     strlen(err_output) + strlen(p) + 1);
+		strcat(err_output, p);
+		free(p);
+	} else
+		err_output = p;
+}	
+
+static bool allocated = false;
+
+bool parse_args(int *argc, char ***argv, ...)
+{
+	char **a;
+	va_list ap;
+
+	va_start(ap, argv);
+	*argc = 1;
+	a = malloc(sizeof(*a) * (*argc + 1));
+	a[0] = (*argv)[0];
+	while ((a[*argc] = va_arg(ap, char *)) != NULL) {
+		(*argc)++;
+		a = realloc(a, sizeof(*a) * (*argc + 1));
+	}
+
+	if (allocated)
+		free(*argv);
+
+	*argv = a;
+	allocated = true;
+	/* Re-set before parsing. */
+	optind = 0;
+
+	return opt_parse(argc, *argv, save_err_output);
+}
+
+struct opt_table short_table[] = {
+	/* Short opts, different args. */
+	OPT_WITHOUT_ARG("-a", test_noarg, "a", "Description of a"),
+	OPT_WITH_ARG("-b", test_arg, show_arg, "b", "Description of b"),
+	OPT_ENDTABLE
+};
+
+struct opt_table long_table[] = {
+	/* Long opts, different args. */
+	OPT_WITHOUT_ARG("--ddd", test_noarg, "ddd", "Description of ddd"),
+	OPT_WITH_ARG("--eee <filename>", test_arg, show_arg, "eee", ""),
+	OPT_ENDTABLE
+};
+
+struct opt_table long_and_short_table[] = {
+	/* Short and long, different args. */
+	OPT_WITHOUT_ARG("--ggg|-g", test_noarg, "ggg", "Description of ggg"),
+	OPT_WITH_ARG("-h|--hhh", test_arg, NULL, "hhh", "Description of hhh"),
+	OPT_ENDTABLE
+};
+
+/* Sub-table test. */
+struct opt_table subtables[] = {
+	/* Two short, and two long long, no description */
+	OPT_WITH_ARG("--jjj|-j|--lll|-l", test_arg, show_arg, "jjj", ""),
+	/* Hidden option */
+	OPT_WITH_ARG("--mmm|-m", test_arg, show_arg, "mmm", opt_hidden),
+	OPT_SUBTABLE(short_table, NULL),
+	OPT_SUBTABLE(long_table, "long table options"),
+	OPT_SUBTABLE(long_and_short_table, NULL),
+	OPT_ENDTABLE
+};

+ 19 - 0
ccan/opt/test/utils.h

@@ -0,0 +1,19 @@
+#ifndef CCAN_OPT_TEST_UTILS_H
+#define CCAN_OPT_TEST_UTILS_H
+#include <ccan/opt/opt.h>
+#include <stdbool.h>
+
+bool parse_args(int *argc, char ***argv, ...);
+extern char *err_output;
+void save_err_output(const char *fmt, ...);
+
+extern unsigned int test_cb_called;
+char *test_noarg(void *arg);
+char *test_arg(const char *optarg, const char *arg);
+void show_arg(char buf[OPT_SHOW_LEN], const char *arg);
+
+extern struct opt_table short_table[];
+extern struct opt_table long_table[];
+extern struct opt_table long_and_short_table[];
+extern struct opt_table subtables[];
+#endif /* CCAN_OPT_TEST_UTILS_H */

+ 111 - 0
ccan/opt/usage.c

@@ -0,0 +1,111 @@
+#include <ccan/opt/opt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include "private.h"
+
+/* We only use this for pointer comparisons. */
+const char opt_hidden[1];
+
+static unsigned write_short_options(char *str)
+{
+	unsigned int i, num = 0;
+	const char *p;
+
+	for (p = first_sopt(&i); p; p = next_sopt(p, &i)) {
+		if (opt_table[i].desc != opt_hidden)
+			str[num++] = *p;
+	}
+	return num;
+}
+
+#define OPT_SPACE_PAD "                    "
+
+/* FIXME: Get all purdy. */
+char *opt_usage(const char *argv0, const char *extra)
+{
+	unsigned int i, num, len;
+	char *ret, *p;
+
+	if (!extra) {
+		extra = "";
+		for (i = 0; i < opt_count; i++) {
+			if (opt_table[i].cb == (void *)opt_usage_and_exit
+			    && opt_table[i].u.carg) {
+				extra = opt_table[i].u.carg;
+				break;
+			}
+		}
+	}
+
+	/* An overestimate of our length. */
+	len = strlen("Usage: %s ") + strlen(argv0)
+		+ strlen("[-%.*s]") + opt_num_short + 1
+		+ strlen(" ") + strlen(extra)
+		+ strlen("\n");
+
+	for (i = 0; i < opt_count; i++) {
+		if (opt_table[i].type == OPT_SUBTABLE) {
+			len += strlen("\n") + strlen(opt_table[i].desc)
+				+ strlen(":\n");
+		} else if (opt_table[i].desc != opt_hidden) {
+			len += strlen(opt_table[i].names) + strlen(" <arg>");
+			len += strlen(OPT_SPACE_PAD)
+				+ strlen(opt_table[i].desc) + 1;
+			if (opt_table[i].show) {
+				len += strlen("(default: %s)")
+					+ OPT_SHOW_LEN + sizeof("...");
+			}
+			len += strlen("\n");
+		}
+	}
+
+	p = ret = malloc(len);
+	if (!ret)
+		return NULL;
+
+	p += sprintf(p, "Usage: %s", argv0);
+	p += sprintf(p, " [-");
+	num = write_short_options(p);
+	if (num) {
+		p += num;
+		p += sprintf(p, "]");
+	} else {
+		/* Remove start of single-entry options */
+		p -= 3;
+	}
+	if (extra)
+		p += sprintf(p, " %s", extra);
+	p += sprintf(p, "\n");
+
+	for (i = 0; i < opt_count; i++) {
+		if (opt_table[i].desc == opt_hidden)
+			continue;
+		if (opt_table[i].type == OPT_SUBTABLE) {
+			p += sprintf(p, "%s:\n", opt_table[i].desc);
+			continue;
+		}
+		len = sprintf(p, "%s", opt_table[i].names);
+		if (opt_table[i].type == OPT_HASARG
+		    && !strchr(opt_table[i].names, ' ')
+		    && !strchr(opt_table[i].names, '='))
+			len += sprintf(p + len, " <arg>");
+		len += sprintf(p + len, "%.*s",
+			       len < strlen(OPT_SPACE_PAD)
+			       ? (unsigned)strlen(OPT_SPACE_PAD) - len : 1,
+			       OPT_SPACE_PAD);
+
+		len += sprintf(p + len, "%s", opt_table[i].desc);
+		if (opt_table[i].show) {
+			char buf[OPT_SHOW_LEN + sizeof("...")];
+			strcpy(buf + OPT_SHOW_LEN, "...");
+			opt_table[i].show(buf, opt_table[i].u.arg);
+			len += sprintf(p + len, " (default: %s)", buf);
+		}
+		p += len;
+		p += sprintf(p, "\n");
+	}
+	*p = '\0';
+	return ret;
+}

+ 510 - 0
ccan/typesafe_cb/LICENSE

@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+

+ 151 - 0
ccan/typesafe_cb/_info

@@ -0,0 +1,151 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * typesafe_cb - macros for safe callbacks.
+ *
+ * The basis of the typesafe_cb header is typesafe_cb_cast(): a
+ * conditional cast macro.   If an expression exactly matches a given
+ * type, it is cast to the target type, otherwise it is left alone.
+ *
+ * This allows us to create functions which take a small number of
+ * specific types, rather than being forced to use a void *.  In
+ * particular, it is useful for creating typesafe callbacks as the
+ * helpers typesafe_cb(), typesafe_cb_preargs() and
+ * typesafe_cb_postargs() demonstrate.
+ * 
+ * The standard way of passing arguments to callback functions in C is
+ * to use a void pointer, which the callback then casts back to the
+ * expected type.  This unfortunately subverts the type checking the
+ * compiler would perform if it were a direct call.  Here's an example:
+ *
+ *	static void my_callback(void *_obj)
+ *	{
+ *		struct obj *obj = _obj;
+ *		...
+ *	}
+ *	...
+ *		register_callback(my_callback, &my_obj);
+ *
+ * If we wanted to use the natural type for my_callback (ie. "void
+ * my_callback(struct obj *obj)"), we could make register_callback()
+ * take a void * as its first argument, but this would subvert all
+ * type checking.  We really want register_callback() to accept only
+ * the exactly correct function type to match the argument, or a
+ * function which takes a void *.
+ *
+ * This is where typesafe_cb() comes in: it uses typesafe_cb_cast() to
+ * cast the callback function if it matches the argument type:
+ *
+ *	void _register_callback(void (*cb)(void *arg), void *arg);
+ *	#define register_callback(cb, arg)				\
+ *		_register_callback(typesafe_cb(void, void *, (cb), (arg)), \
+ *				   (arg))
+ *
+ * On compilers which don't support the extensions required
+ * typesafe_cb_cast() and friend become an unconditional cast, so your
+ * code will compile but you won't get type checking.
+ *
+ * Example:
+ *	#include <ccan/typesafe_cb/typesafe_cb.h>
+ *	#include <stdlib.h>
+ *	#include <stdio.h>
+ *
+ *	// Generic callback infrastructure.
+ *	struct callback {
+ *		struct callback *next;
+ *		int value;
+ *		int (*callback)(int value, void *arg);
+ *		void *arg;
+ *	};
+ *	static struct callback *callbacks;
+ *	
+ *	static void _register_callback(int value, int (*cb)(int, void *),
+ *				       void *arg)
+ *	{
+ *		struct callback *new = malloc(sizeof(*new));
+ *		new->next = callbacks;
+ *		new->value = value;
+ *		new->callback = cb;
+ *		new->arg = arg;
+ *		callbacks = new;
+ *	}
+ *	#define register_callback(value, cb, arg)			\
+ *		_register_callback(value,				\
+ *				   typesafe_cb_preargs(int, void *,	\
+ *						       (cb), (arg), int),\
+ *				   (arg))
+ *	
+ *	static struct callback *find_callback(int value)
+ *	{
+ *		struct callback *i;
+ *	
+ *		for (i = callbacks; i; i = i->next)
+ *			if (i->value == value)
+ *				return i;
+ *		return NULL;
+ *	}   
+ *
+ *	// Define several silly callbacks.  Note they don't use void *!
+ *	#define DEF_CALLBACK(name, op)			\
+ *		static int name(int val, int *arg)	\
+ *		{					\
+ *			printf("%s", #op);		\
+ *			return val op *arg;		\
+ *		}
+ *	DEF_CALLBACK(multiply, *);
+ *	DEF_CALLBACK(add, +);
+ *	DEF_CALLBACK(divide, /);
+ *	DEF_CALLBACK(sub, -);
+ *	DEF_CALLBACK(or, |);
+ *	DEF_CALLBACK(and, &);
+ *	DEF_CALLBACK(xor, ^);
+ *	DEF_CALLBACK(assign, =);
+ *
+ *	// Silly game to find the longest chain of values.
+ *	int main(int argc, char *argv[])
+ *	{
+ *		int i, run = 1, num = argv[1] ? atoi(argv[1]) : 0;
+ *	
+ *		for (i = 1; i < 1024;) {
+ *			// Since run is an int, compiler checks "add" does too.
+ *			register_callback(i++, add, &run);
+ *			register_callback(i++, divide, &run);
+ *			register_callback(i++, sub, &run);
+ *			register_callback(i++, multiply, &run);
+ *			register_callback(i++, or, &run);
+ *			register_callback(i++, and, &run);
+ *			register_callback(i++, xor, &run);
+ *			register_callback(i++, assign, &run);
+ *		}
+ *	
+ *		printf("%i ", num);
+ *		while (run < 56) {
+ *			struct callback *cb = find_callback(num % i);
+ *			if (!cb) {
+ *				printf("-> STOP\n");
+ *				return 1;
+ *			}
+ *			num = cb->callback(num, cb->arg);
+ *			printf("->%i ", num);
+ *			run++;
+ *		}
+ *		printf("-> Winner!\n");
+ *		return 0;
+ *	}
+ *
+ * License: LGPL (2 or any later version)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+	if (argc != 2)
+		return 1;
+
+	if (strcmp(argv[1], "depends") == 0) {
+		return 0;
+	}
+
+	return 1;
+}

+ 23 - 0
ccan/typesafe_cb/test/compile_fail-cast_if_type-promotable.c

@@ -0,0 +1,23 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdbool.h>
+
+static void _set_some_value(void *val)
+{
+}
+
+#define set_some_value(expr)						\
+	_set_some_value(typesafe_cb_cast(void *, long, (expr)))
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+	bool x = 0;
+#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
+#endif
+#else
+	long x = 0;
+#endif
+	set_some_value(x);
+	return 0;
+}

+ 27 - 0
ccan/typesafe_cb/test/compile_fail-typesafe_cb-int.c

@@ -0,0 +1,27 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdlib.h>
+
+void _callback(void (*fn)(void *arg), void *arg);
+void _callback(void (*fn)(void *arg), void *arg)
+{
+	fn(arg);
+}
+
+/* Callback is set up to warn if arg isn't a pointer (since it won't
+ * pass cleanly to _callback's second arg. */
+#define callback(fn, arg)						\
+	_callback(typesafe_cb(void, (fn), (arg)), (arg))
+
+void my_callback(int something);
+void my_callback(int something)
+{
+}
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+	/* This fails due to arg, not due to cast. */
+	callback(my_callback, 100);
+#endif
+	return 0;
+}

+ 34 - 0
ccan/typesafe_cb/test/compile_fail-typesafe_cb.c

@@ -0,0 +1,34 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdlib.h>
+
+static void _register_callback(void (*cb)(void *arg), void *arg)
+{
+}
+
+#define register_callback(cb, arg)				\
+	_register_callback(typesafe_cb(void, void *, (cb), (arg)), (arg))
+
+static void my_callback(char *p)
+{
+}
+
+int main(int argc, char *argv[])
+{
+	char str[] = "hello world";
+#ifdef FAIL
+	int *p;
+#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
+#endif
+#else
+	char *p;
+#endif
+	p = NULL;
+
+	/* This should work always. */
+	register_callback(my_callback, str);
+
+	/* This will fail with FAIL defined */
+	register_callback(my_callback, p);
+	return 0;
+}

+ 43 - 0
ccan/typesafe_cb/test/compile_fail-typesafe_cb_cast-multi.c

@@ -0,0 +1,43 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdlib.h>
+
+struct foo {
+	int x;
+};
+
+struct bar {
+	int x;
+};
+
+struct baz {
+	int x;
+};
+
+struct any {
+	int x;
+};
+
+struct other {
+	int x;
+};
+
+static void take_any(struct any *any)
+{
+}
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+	struct other
+#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
+#endif
+#else
+	struct foo
+#endif
+		*arg = NULL;
+	take_any(typesafe_cb_cast3(struct any *,
+				   struct foo *, struct bar *, struct baz *,
+				   arg));
+	return 0;
+}

+ 25 - 0
ccan/typesafe_cb/test/compile_fail-typesafe_cb_cast.c

@@ -0,0 +1,25 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+
+void _set_some_value(void *val);
+
+void _set_some_value(void *val)
+{
+}
+
+#define set_some_value(expr)						\
+	_set_some_value(typesafe_cb_cast(void *, unsigned long, (expr)))
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+	int x = 0;
+	set_some_value(x);
+#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
+#endif
+#else
+	void *p = 0;
+	set_some_value(p);
+#endif
+	return 0;
+}

+ 27 - 0
ccan/typesafe_cb/test/compile_fail-typesafe_cb_postargs.c

@@ -0,0 +1,27 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdlib.h>
+
+static void _register_callback(void (*cb)(void *arg, int x), void *arg)
+{
+}
+#define register_callback(cb, arg)				\
+	_register_callback(typesafe_cb_postargs(void, void *, (cb), (arg), int), (arg))
+
+static void my_callback(char *p, int x)
+{
+}
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+	int *p;
+#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
+#endif
+#else
+	char *p;
+#endif
+	p = NULL;
+	register_callback(my_callback, p);
+	return 0;
+}

+ 28 - 0
ccan/typesafe_cb/test/compile_fail-typesafe_cb_preargs.c

@@ -0,0 +1,28 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdlib.h>
+
+static void _register_callback(void (*cb)(int x, void *arg), void *arg)
+{
+}
+
+#define register_callback(cb, arg)				\
+	_register_callback(typesafe_cb_preargs(void, void *, (cb), (arg), int), (arg))
+
+static void my_callback(int x, char *p)
+{
+}
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+	int *p;
+#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
+#endif
+#else
+	char *p;
+#endif
+	p = NULL;
+	register_callback(my_callback, p);
+	return 0;
+}

+ 17 - 0
ccan/typesafe_cb/test/compile_ok-typesafe_cb-NULL.c

@@ -0,0 +1,17 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdlib.h>
+
+/* NULL args for callback function should be OK for normal and _def. */
+
+static void _register_callback(void (*cb)(const void *arg), const void *arg)
+{
+}
+
+#define register_callback(cb, arg)				\
+	_register_callback(typesafe_cb(void, const void *, (cb), (arg)), (arg))
+
+int main(int argc, char *argv[])
+{
+	register_callback(NULL, "hello world");
+	return 0;
+}

+ 49 - 0
ccan/typesafe_cb/test/compile_ok-typesafe_cb-undefined.c

@@ -0,0 +1,49 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdlib.h>
+
+/* const args in callbacks should be OK. */
+
+static void _register_callback(void (*cb)(void *arg), void *arg)
+{
+}
+
+#define register_callback(cb, arg)				\
+	_register_callback(typesafe_cb(void, void *, (cb), (arg)), (arg))
+
+static void _register_callback_pre(void (*cb)(int x, void *arg), void *arg)
+{
+}
+
+#define register_callback_pre(cb, arg)					\
+	_register_callback_pre(typesafe_cb_preargs(void, void *, (cb), (arg), int), (arg))
+
+static void _register_callback_post(void (*cb)(void *arg, int x), void *arg)
+{
+}
+
+#define register_callback_post(cb, arg)					\
+	_register_callback_post(typesafe_cb_postargs(void, void *, (cb), (arg), int), (arg))
+
+struct undefined;
+
+static void my_callback(struct undefined *undef)
+{
+}
+
+static void my_callback_pre(int x, struct undefined *undef)
+{
+}
+
+static void my_callback_post(struct undefined *undef, int x)
+{
+}
+
+int main(int argc, char *argv[])
+{
+	struct undefined *handle = NULL;
+
+	register_callback(my_callback, handle);
+	register_callback_pre(my_callback_pre, handle);
+	register_callback_post(my_callback_post, handle);
+	return 0;
+}

+ 52 - 0
ccan/typesafe_cb/test/compile_ok-typesafe_cb-vars.c

@@ -0,0 +1,52 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdlib.h>
+
+/* const args in callbacks should be OK. */
+
+static void _register_callback(void (*cb)(void *arg), void *arg)
+{
+}
+
+#define register_callback(cb, arg)				\
+	_register_callback(typesafe_cb(void, void *, (cb), (arg)), (arg))
+
+static void _register_callback_pre(void (*cb)(int x, void *arg), void *arg)
+{
+}
+
+#define register_callback_pre(cb, arg)					\
+	_register_callback_pre(typesafe_cb_preargs(void, void *, (cb), (arg), int), (arg))
+
+static void _register_callback_post(void (*cb)(void *arg, int x), void *arg)
+{
+}
+
+#define register_callback_post(cb, arg)					\
+	_register_callback_post(typesafe_cb_postargs(void, void *, (cb), (arg), int), (arg))
+
+struct undefined;
+
+static void my_callback(struct undefined *undef)
+{
+}
+
+static void my_callback_pre(int x, struct undefined *undef)
+{
+}
+
+static void my_callback_post(struct undefined *undef, int x)
+{
+}
+
+int main(int argc, char *argv[])
+{
+	struct undefined *handle = NULL;
+	void (*cb)(struct undefined *undef) = my_callback;
+	void (*pre)(int x, struct undefined *undef) = my_callback_pre;
+	void (*post)(struct undefined *undef, int x) = my_callback_post;
+
+	register_callback(cb, handle);
+	register_callback_pre(pre, handle);
+	register_callback_post(post, handle);
+	return 0;
+}

+ 41 - 0
ccan/typesafe_cb/test/compile_ok-typesafe_cb_cast.c

@@ -0,0 +1,41 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <stdlib.h>
+
+struct foo {
+	int x;
+};
+
+struct bar {
+	int x;
+};
+
+struct baz {
+	int x;
+};
+
+struct any {
+	int x;
+};
+
+static void take_any(struct any *any)
+{
+}
+
+int main(int argc, char *argv[])
+{
+	/* Otherwise we get unused warnings for these. */
+	struct foo *foo = NULL;
+	struct bar *bar = NULL;
+	struct baz *baz = NULL;
+
+	take_any(typesafe_cb_cast3(struct any *,
+				   struct foo *, struct bar *, struct baz *,
+				   foo));
+	take_any(typesafe_cb_cast3(struct any *, 
+				   struct foo *, struct bar *, struct baz *,
+				   bar));
+	take_any(typesafe_cb_cast3(struct any *, 
+				   struct foo *, struct bar *, struct baz *,
+				   baz));
+	return 0;
+}

+ 109 - 0
ccan/typesafe_cb/test/run.c

@@ -0,0 +1,109 @@
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <string.h>
+#include <stdint.h>
+#include <ccan/tap/tap.h>
+
+static char dummy = 0;
+
+/* The example usage. */
+static void _set_some_value(void *val)
+{
+	ok1(val == &dummy);
+}
+
+#define set_some_value(expr)						\
+	_set_some_value(typesafe_cb_cast(void *, unsigned long, (expr)))
+
+static void _callback_onearg(void (*fn)(void *arg), void *arg)
+{
+	fn(arg);
+}
+
+static void _callback_preargs(void (*fn)(int a, int b, void *arg), void *arg)
+{
+	fn(1, 2, arg);
+}
+
+static void _callback_postargs(void (*fn)(void *arg, int a, int b), void *arg)
+{
+	fn(arg, 1, 2);
+}
+
+#define callback_onearg(cb, arg)					\
+	_callback_onearg(typesafe_cb(void, void *, (cb), (arg)), (arg))
+
+#define callback_preargs(cb, arg)					\
+	_callback_preargs(typesafe_cb_preargs(void, void *, (cb), (arg), int, int), (arg))
+
+#define callback_postargs(cb, arg)					\
+	_callback_postargs(typesafe_cb_postargs(void, void *, (cb), (arg), int, int), (arg))
+
+static void my_callback_onearg(char *p)
+{
+	ok1(strcmp(p, "hello world") == 0);
+}
+
+static void my_callback_preargs(int a, int b, char *p)
+{
+	ok1(a == 1);
+	ok1(b == 2);
+	ok1(strcmp(p, "hello world") == 0);
+}
+
+static void my_callback_postargs(char *p, int a, int b)
+{
+	ok1(a == 1);
+	ok1(b == 2);
+	ok1(strcmp(p, "hello world") == 0);
+}
+
+/* This is simply a compile test; we promised typesafe_cb_cast can be in a
+ * static initializer. */
+struct callback_onearg
+{
+	void (*fn)(void *arg);
+	const void *arg;
+};
+
+struct callback_onearg cb_onearg
+= { typesafe_cb(void, void *, my_callback_onearg, (char *)(intptr_t)"hello world"),
+    "hello world" };
+
+struct callback_preargs
+{
+	void (*fn)(int a, int b, void *arg);
+	const void *arg;
+};
+
+struct callback_preargs cb_preargs
+= { typesafe_cb_preargs(void, void *, my_callback_preargs,
+			(char *)(intptr_t)"hi", int, int), "hi" };
+
+struct callback_postargs
+{
+	void (*fn)(void *arg, int a, int b);
+	const void *arg;
+};
+
+struct callback_postargs cb_postargs
+= { typesafe_cb_postargs(void, void *, my_callback_postargs, 
+			 (char *)(intptr_t)"hi", int, int), "hi" };
+
+int main(int argc, char *argv[])
+{
+	void *p = &dummy;
+	unsigned long l = (unsigned long)p;
+	char str[] = "hello world";
+
+	plan_tests(2 + 1 + 3 + 3);
+	set_some_value(p);
+	set_some_value(l);
+
+	callback_onearg(my_callback_onearg, str);
+
+	callback_preargs(my_callback_preargs, str);
+
+	callback_postargs(my_callback_postargs, str);
+
+	return exit_status();
+}

+ 133 - 0
ccan/typesafe_cb/typesafe_cb.h

@@ -0,0 +1,133 @@
+#ifndef CCAN_TYPESAFE_CB_H
+#define CCAN_TYPESAFE_CB_H
+#include "config.h"
+
+#if HAVE_TYPEOF && HAVE_BUILTIN_CHOOSE_EXPR && HAVE_BUILTIN_TYPES_COMPATIBLE_P
+/**
+ * typesafe_cb_cast - only cast an expression if it matches a given type
+ * @desttype: the type to cast to
+ * @oktype: the type we allow
+ * @expr: the expression to cast
+ *
+ * This macro is used to create functions which allow multiple types.
+ * The result of this macro is used somewhere that a @desttype type is
+ * expected: if @expr is exactly of type @oktype, then it will be
+ * cast to @desttype type, otherwise left alone.
+ *
+ * This macro can be used in static initializers.
+ *
+ * This is merely useful for warnings: if the compiler does not
+ * support the primitives required for typesafe_cb_cast(), it becomes an
+ * unconditional cast, and the @oktype argument is not used.  In
+ * particular, this means that @oktype can be a type which uses the
+ * "typeof": it will not be evaluated if typeof is not supported.
+ *
+ * Example:
+ *	// We can take either an unsigned long or a void *.
+ *	void _set_some_value(void *val);
+ *	#define set_some_value(e)			\
+ *		_set_some_value(typesafe_cb_cast(void *, (e), unsigned long))
+ */
+#define typesafe_cb_cast(desttype, oktype, expr)			\
+	__builtin_choose_expr(						\
+		__builtin_types_compatible_p(__typeof__(0?(expr):(expr)), \
+					     oktype),			\
+		(desttype)(expr), (expr))
+#else
+#define typesafe_cb_cast(desttype, oktype, expr) ((desttype)(expr))
+#endif
+
+/**
+ * typesafe_cb_cast3 - only cast an expression if it matches given types
+ * @desttype: the type to cast to
+ * @ok1: the first type we allow
+ * @ok2: the second type we allow
+ * @ok3: the third type we allow
+ * @expr: the expression to cast
+ *
+ * This is a convenient wrapper for multiple typesafe_cb_cast() calls.
+ * You can chain them inside each other (ie. use typesafe_cb_cast()
+ * for expr) if you need more than 3 arguments.
+ *
+ * Example:
+ *	// We can take either a long, unsigned long, void * or a const void *.
+ *	void _set_some_value(void *val);
+ *	#define set_some_value(expr)					\
+ *		_set_some_value(typesafe_cb_cast3(void *,,		\
+ *					    long, unsigned long, const void *,\
+ *					    (expr)))
+ */
+#define typesafe_cb_cast3(desttype, ok1, ok2, ok3, expr)		\
+	typesafe_cb_cast(desttype, ok1,					\
+			 typesafe_cb_cast(desttype, ok2,		\
+					  typesafe_cb_cast(desttype, ok3, \
+							   (expr))))
+
+/**
+ * typesafe_cb - cast a callback function if it matches the arg
+ * @rtype: the return type of the callback function
+ * @atype: the (pointer) type which the callback function expects.
+ * @fn: the callback function to cast
+ * @arg: the (pointer) argument to hand to the callback function.
+ *
+ * If a callback function takes a single argument, this macro does
+ * appropriate casts to a function which takes a single atype argument if the
+ * callback provided matches the @arg.
+ *
+ * It is assumed that @arg is of pointer type: usually @arg is passed
+ * or assigned to a void * elsewhere anyway.
+ *
+ * Example:
+ *	void _register_callback(void (*fn)(void *arg), void *arg);
+ *	#define register_callback(fn, arg) \
+ *		_register_callback(typesafe_cb(void, (fn), void*, (arg)), (arg))
+ */
+#define typesafe_cb(rtype, atype, fn, arg)			\
+	typesafe_cb_cast(rtype (*)(atype),			\
+			 rtype (*)(__typeof__(arg)),		\
+			 (fn))
+
+/**
+ * typesafe_cb_preargs - cast a callback function if it matches the arg
+ * @rtype: the return type of the callback function
+ * @atype: the (pointer) type which the callback function expects.
+ * @fn: the callback function to cast
+ * @arg: the (pointer) argument to hand to the callback function.
+ *
+ * This is a version of typesafe_cb() for callbacks that take other arguments
+ * before the @arg.
+ *
+ * Example:
+ *	void _register_callback(void (*fn)(int, void *arg), void *arg);
+ *	#define register_callback(fn, arg)				   \
+ *		_register_callback(typesafe_cb_preargs(void, (fn), void *, \
+ *				   (arg), int),				   \
+ *				   (arg))
+ */
+#define typesafe_cb_preargs(rtype, atype, fn, arg, ...)			\
+	typesafe_cb_cast(rtype (*)(__VA_ARGS__, atype),			\
+			 rtype (*)(__VA_ARGS__, __typeof__(arg)),	\
+			 (fn))
+
+/**
+ * typesafe_cb_postargs - cast a callback function if it matches the arg
+ * @rtype: the return type of the callback function
+ * @atype: the (pointer) type which the callback function expects.
+ * @fn: the callback function to cast
+ * @arg: the (pointer) argument to hand to the callback function.
+ *
+ * This is a version of typesafe_cb() for callbacks that take other arguments
+ * after the @arg.
+ *
+ * Example:
+ *	void _register_callback(void (*fn)(void *arg, int), void *arg);
+ *	#define register_callback(fn, arg) \
+ *		_register_callback(typesafe_cb_postargs(void, (fn), void *, \
+ *				   (arg), int),				    \
+ *				   (arg))
+ */
+#define typesafe_cb_postargs(rtype, atype, fn, arg, ...)		\
+	typesafe_cb_cast(rtype (*)(atype, __VA_ARGS__),			\
+			 rtype (*)(__typeof__(arg), __VA_ARGS__),	\
+			 (fn))
+#endif /* CCAN_CAST_IF_TYPE_H */

+ 11652 - 0
cgminer.c

@@ -0,0 +1,11652 @@
+/*
+ * Copyright 2011-2022 Andrew Smith
+ * Copyright 2011-2018 Con Kolivas
+ * Copyright 2011-2012 Luke Dashjr
+ * Copyright 2010 Jeff Garzik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "config.h"
+
+#ifdef __GNUC__
+#if __GNUC__ >= 7
+#pragma GCC diagnostic ignored "-Wcast-function-type"
+#pragma GCC diagnostic ignored "-Wstringop-truncation"
+#endif
+#endif
+
+#ifdef HAVE_CURSES
+#include <curses.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <signal.h>
+#include <limits.h>
+
+#ifdef USE_USBUTILS
+#include <semaphore.h>
+#endif
+
+#ifdef USE_LIBSYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef WIN32
+#include <sys/resource.h>
+#else
+#include <winsock2.h>
+#include <windows.h>
+#endif
+#include <ccan/opt/opt.h>
+#include <jansson.h>
+#ifdef HAVE_LIBCURL
+#include <curl/curl.h>
+#else
+char *curly = ":D";
+#endif
+#include <libgen.h>
+#include <sha2.h>
+
+#include "compat.h"
+#include "miner.h"
+#include "bench_block.h"
+#ifdef USE_USBUTILS
+#include "usbutils.h"
+#endif
+
+#if defined(unix) || defined(__APPLE__)
+	#include <errno.h>
+	#include <fcntl.h>
+	#include <sys/wait.h>
+#endif
+
+#ifdef USE_AVALON
+#include "driver-avalon.h"
+#endif
+
+#ifdef USE_AVALON2
+#include "driver-avalon2.h"
+#endif
+
+#ifdef USE_AVALON4
+#include "driver-avalon4.h"
+#endif
+
+#ifdef USE_AVALON7
+#include "driver-avalon7.h"
+#include "libssplus.h"
+#endif
+
+#ifdef USE_AVALON8
+#include "driver-avalon8.h"
+#endif
+
+#ifdef USE_AVALON9
+#include "driver-avalon9.h"
+#endif
+
+#ifdef USE_AVALONLC3
+#include "driver-avalonlc3.h"
+#endif
+
+#ifdef USE_AVALON_MINER
+#include "driver-avalon-miner.h"
+#endif
+
+#ifdef USE_BFLSC
+#include "driver-bflsc.h"
+#endif
+
+#ifdef USE_SP10
+#include "driver-spondoolies-sp10.h"
+#endif
+
+#ifdef USE_SP30
+#include "driver-spondoolies-sp30.h"
+#endif
+
+#ifdef USE_BLOCK_ERUPTER
+#include "driver-blockerupter.h"
+#endif
+
+#ifdef USE_BITFURY
+#include "driver-bitfury.h"
+#endif
+
+#ifdef USE_BITFURY16
+#include "driver-bitfury16.h"
+#endif
+
+#ifdef USE_BITMAIN_SOC
+#include <sys/sysinfo.h>
+#include "driver-btm-soc.h"
+#endif
+
+
+#ifdef USE_COINTERRA
+#include "driver-cointerra.h"
+#endif
+
+#ifdef USE_GEKKO
+#include "driver-gekko.h"
+#endif
+
+#ifdef USE_HASHFAST
+#include "driver-hashfast.h"
+#endif
+
+#if defined(USE_ANT_S1) || defined(USE_ANT_S2) || defined(USE_ANT_S3)
+#include "driver-bitmain.h"
+#endif
+
+#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_AVALON) || defined(USE_AVALON2) || defined(USE_MODMINER)
+#	define USE_FPGA
+#endif
+
+struct strategies strategies[] = {
+	{ "Failover" },
+	{ "Round Robin" },
+	{ "Rotate" },
+	{ "Load Balance" },
+	{ "Balance" },
+};
+
+static char packagename[256];
+
+bool opt_work_update;
+#if defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_AVALON9) || defined(USE_AVALONLC3)
+bool opt_clean_jobs = false;
+#endif
+bool opt_protocol;
+static struct benchfile_layout {
+	int length;
+	char *name;
+} benchfile_data[] = {
+	{ 1,	"Version" },
+	{ 64,	"MerkleRoot" },
+	{ 64,	"PrevHash" },
+	{ 8,	"DifficultyBits" },
+	{ 10,	"NonceTime" } // 10 digits
+};
+enum benchwork {
+	BENCHWORK_VERSION = 0,
+	BENCHWORK_MERKLEROOT,
+	BENCHWORK_PREVHASH,
+	BENCHWORK_DIFFBITS,
+	BENCHWORK_NONCETIME,
+	BENCHWORK_COUNT
+};
+
+#ifdef HAVE_LIBCURL
+static char *opt_btc_address;
+static char *opt_btc_sig;
+#endif
+struct pool *opt_btcd;
+static char *opt_benchfile;
+static bool opt_benchfile_display;
+static FILE *benchfile_in;
+static int benchfile_line;
+static int benchfile_work;
+static bool opt_benchmark;
+static bool opt_blockcheck;
+bool have_longpoll;
+bool want_per_device_stats;
+bool use_syslog;
+bool opt_quiet;
+bool opt_realquiet;
+bool opt_loginput;
+bool opt_compact;
+bool opt_decode;
+const int opt_cutofftemp = 95;
+int opt_log_interval = 5;
+static const int max_queue = 1;
+const int max_scantime = 60;
+const int max_expiry = 600;
+uint64_t global_hashrate;
+unsigned long global_quota_gcd = 1;
+time_t last_getwork;
+int opt_pool_fallback = 120;
+
+#if defined(USE_USBUTILS)
+int nDevs;
+#endif
+bool opt_restart = true;
+bool opt_nogpu;
+
+struct list_head scan_devices;
+static bool opt_display_devs;
+int total_devices;
+int zombie_devs;
+static int most_devices;
+struct cgpu_info **devices;
+int mining_threads;
+int num_processors;
+#ifdef HAVE_CURSES
+bool use_curses = true;
+#else
+bool use_curses;
+#endif
+bool opt_mac_yield;
+bool opt_widescreen;
+static bool alt_status;
+static bool switch_status;
+static bool opt_submit_stale = true;
+static int opt_shares;
+static bool opt_fix_protocol;
+bool opt_lowmem;
+bool opt_autofan;
+bool opt_autoengine;
+bool opt_noadl;
+char *opt_api_allow = NULL;
+char *opt_api_groups;
+char *opt_api_description = PACKAGE_STRING;
+int opt_api_port = 4028;
+char *opt_api_host = API_LISTEN_ADDR;
+bool opt_api_listen;
+bool opt_api_mcast;
+char *opt_api_mcast_addr = API_MCAST_ADDR;
+char *opt_api_mcast_code = API_MCAST_CODE;
+char *opt_api_mcast_des = "";
+int opt_api_mcast_port = 4028;
+bool opt_api_network;
+bool opt_delaynet;
+bool opt_disable_pool;
+static bool no_work;
+#ifdef USE_ICARUS
+char *opt_icarus_options = NULL;
+char *opt_icarus_timing = NULL;
+float opt_anu_freq = 250;
+float opt_au3_freq = 225;
+int opt_au3_volt = 775;
+float opt_rock_freq = 270;
+#endif
+bool opt_worktime;
+#ifdef USE_AVALON
+char *opt_avalon_options;
+char *opt_bitburner_fury_options;
+static char *opt_set_avalon_fan;
+static char *opt_set_avalon_freq;
+#endif
+#ifdef USE_AVALON2
+static char *opt_set_avalon2_freq;
+static char *opt_set_avalon2_fan;
+static char *opt_set_avalon2_voltage;
+#endif
+#ifdef USE_AVALON4
+static char *opt_set_avalon4_fan;
+static char *opt_set_avalon4_voltage;
+static char *opt_set_avalon4_freq;
+#endif
+#ifdef USE_AVALON7
+static char *opt_set_avalon7_fan;
+static char *opt_set_avalon7_voltage;
+static char *opt_set_avalon7_voltage_level;
+static char *opt_set_avalon7_voltage_offset;
+static char *opt_set_avalon7_freq;
+#endif
+#ifdef USE_AVALON8
+static char *opt_set_avalon8_fan;
+static char *opt_set_avalon8_voltage_level;
+static char *opt_set_avalon8_voltage_level_offset;
+static char *opt_set_avalon8_freq;
+static char *opt_set_avalon8_asic_otp;
+#endif
+#ifdef USE_AVALON9
+static char *opt_set_avalon9_fan;
+static char *opt_set_avalon9_voltage_level;
+static char *opt_set_avalon9_voltage_level_offset;
+static char *opt_set_avalon9_freq;
+static char *opt_set_avalon9_adjust_volt_info;
+#endif
+#ifdef USE_AVALONLC3
+static char *opt_set_avalonlc3_fan;
+static char *opt_set_avalonlc3_voltage_level;
+static char *opt_set_avalonlc3_voltage_level_offset;
+static char *opt_set_avalonlc3_freq;
+static char *opt_set_avalonlc3_asic_otp;
+#endif
+#ifdef USE_AVALON_MINER
+static char *opt_set_avalonm_voltage;
+static char *opt_set_avalonm_freq;
+#endif
+#ifdef USE_BLOCKERUPTER
+int opt_bet_clk = 0;
+#endif
+#ifdef USE_GEKKO
+char *opt_gekko_serial = NULL;
+bool opt_gekko_noboost = 0;
+bool opt_gekko_lowboost = 0;
+bool opt_gekko_gsc_detect = 0;
+bool opt_gekko_gsd_detect = 0;
+bool opt_gekko_gse_detect = 0;
+bool opt_gekko_gsh_detect = 0;
+bool opt_gekko_gsi_detect = 0;
+bool opt_gekko_gsf_detect = 0;
+bool opt_gekko_r909_detect = 0;
+float opt_gekko_gsc_freq = 150;
+float opt_gekko_gsd_freq = 100;
+float opt_gekko_gse_freq = 150;
+float opt_gekko_tune_up = 97;
+float opt_gekko_tune_down = 95;
+#if defined(__APPLE__)
+float opt_gekko_wait_factor = 0.3;
+#elif defined (WIN32)
+float opt_gekko_wait_factor = 0.4;
+#else
+float opt_gekko_wait_factor = 0.5;
+#endif
+float opt_gekko_step_freq = 6.25;
+int opt_gekko_gsh_freq = 100;
+int opt_gekko_gsi_freq = 550;
+int opt_gekko_gsf_freq = 200;
+int opt_gekko_r909_freq = 450;
+int opt_gekko_bauddiv = 0;
+int opt_gekko_gsh_vcore = 400;
+int opt_gekko_start_freq = 100;
+int opt_gekko_step_delay = 15;
+bool opt_gekko_mine2 = false; // gekko code ignores it
+int opt_gekko_tune2 = 0;
+#endif
+#ifdef USE_HASHRATIO
+#include "driver-hashratio.h"
+#endif
+#ifdef USE_KLONDIKE
+char *opt_klondike_options = NULL;
+#endif
+#ifdef USE_DRILLBIT
+char *opt_drillbit_options = NULL;
+char *opt_drillbit_auto = NULL;
+#endif
+char *opt_bab_options = NULL;
+#ifdef USE_BITMINE_A1
+char *opt_bitmine_a1_options = NULL;
+#endif
+#ifdef USE_DRAGONMINT_T1
+#include "dragonmint_t1.h"
+char *opt_dragonmint_t1_options = NULL;
+int opt_T1Pll[MCOMPAT_CONFIG_MAX_CHAIN_NUM] = {
+	DEFAULT_PLL, DEFAULT_PLL, DEFAULT_PLL, DEFAULT_PLL, 
+	DEFAULT_PLL, DEFAULT_PLL, DEFAULT_PLL, DEFAULT_PLL
+};
+int opt_T1Vol[MCOMPAT_CONFIG_MAX_CHAIN_NUM] = {
+	DEFAULT_VOLT, DEFAULT_VOLT, DEFAULT_VOLT, DEFAULT_VOLT,
+	DEFAULT_VOLT, DEFAULT_VOLT, DEFAULT_VOLT, DEFAULT_VOLT
+};
+int opt_T1VID[MCOMPAT_CONFIG_MAX_CHAIN_NUM] = {};
+bool opt_T1auto = true;
+bool opt_T1_efficient;
+bool opt_T1_performance;
+int opt_T1_target = 100;
+#endif
+#if defined(USE_ANT_S1) || defined(USE_ANT_S2)
+char *opt_bitmain_options;
+char *opt_set_bitmain_fan;
+char *opt_bitmain_freq;
+// Ignored
+bool opt_bitmain_nobeeper;
+bool opt_bitmain_notempoverctrl;
+bool opt_bitmain_homemode;
+#endif
+#ifdef USE_ANT_S2
+#ifndef USE_ANT_S3
+char *opt_bitmain_dev;
+#endif
+char *opt_bitmain_voltage = BITMAIN_VOLTAGE_DEF;
+#endif
+#ifdef USE_HASHFAST
+static char *opt_set_hfa_fan;
+#endif
+static char *opt_set_null;
+#ifdef USE_MINION
+int opt_minion_chipreport;
+char *opt_minion_cores;
+bool opt_minion_extra;
+char *opt_minion_freq;
+int opt_minion_freqchange = 1000;
+int opt_minion_freqpercent = 70;
+bool opt_minion_idlecount;
+int opt_minion_ledcount;
+int opt_minion_ledlimit = 98;
+bool opt_minion_noautofreq;
+bool opt_minion_overheat;
+int opt_minion_spidelay;
+char *opt_minion_spireset;
+int opt_minion_spisleep = 200;
+int opt_minion_spiusec;
+char *opt_minion_temp;
+#endif
+
+#ifdef USE_USBUTILS
+char *opt_usb_select = NULL;
+int opt_usbdump = -1;
+bool opt_usb_list_all;
+cgsem_t usb_resource_sem;
+static pthread_t usb_poll_thread;
+static bool usb_polling;
+static bool polling_usb;
+static bool usb_reinit;
+#endif
+
+char *opt_kernel_path;
+char *cgminer_path;
+bool opt_gen_stratum_work;
+
+#if defined(USE_BITFORCE)
+bool opt_bfl_noncerange;
+#endif
+#define QUIET	(opt_quiet || opt_realquiet)
+
+struct thr_info *control_thr;
+struct thr_info **mining_thr;
+static int gwsched_thr_id;
+static int watchpool_thr_id;
+static int watchdog_thr_id;
+#ifdef HAVE_CURSES
+static int input_thr_id;
+#endif
+int gpur_thr_id;
+static int api_thr_id;
+#ifdef USE_USBUTILS
+static int usbres_thr_id;
+static int hotplug_thr_id;
+#endif
+static int total_control_threads;
+bool hotplug_mode;
+static int new_devices;
+static int new_threads;
+int hotplug_time = 5;
+
+#if LOCK_TRACKING
+pthread_mutex_t lockstat_lock;
+#endif
+
+pthread_mutex_t hash_lock;
+static pthread_mutex_t *stgd_lock;
+pthread_mutex_t console_lock;
+cglock_t ch_lock;
+static pthread_rwlock_t blk_lock;
+static pthread_mutex_t sshare_lock;
+
+pthread_rwlock_t netacc_lock;
+pthread_rwlock_t mining_thr_lock;
+pthread_rwlock_t devices_lock;
+
+static pthread_mutex_t lp_lock;
+static pthread_cond_t lp_cond;
+
+pthread_mutex_t restart_lock;
+pthread_cond_t restart_cond;
+
+pthread_cond_t gws_cond;
+
+double rolling1, rolling5, rolling15;
+double total_rolling;
+double total_mhashes_done;
+
+#ifdef USE_BITMAIN_SOC
+char *opt_version_path = NULL;
+char displayed_hash_rate[16] = {0};
+char nonce_num10_string[NONCE_BUFF];
+char nonce_num30_string[NONCE_BUFF];
+char nonce_num60_string[NONCE_BUFF];
+char g_miner_version[256] = {0};
+char g_miner_compiletime[256] = {0};
+char g_miner_type[256] = {0};
+
+double new_total_mhashes_done;
+double new_total_secs = 1.0;
+// only used for total_secs, because we need use system info time, instead of real data time.
+time_t total_tv_start_sys;
+time_t total_tv_end_sys;
+#endif
+
+static struct timeval total_tv_start, total_tv_end;
+static struct timeval restart_tv_start, update_tv_start;
+
+cglock_t control_lock;
+pthread_mutex_t stats_lock;
+
+int hw_errors;
+int64_t total_accepted, total_rejected, total_diff1;
+int64_t total_getworks, total_stale, total_discarded;
+double total_diff_accepted, total_diff_rejected, total_diff_stale;
+static int staged_rollable;
+unsigned int new_blocks;
+static unsigned int work_block;
+unsigned int found_blocks;
+
+unsigned int local_work;
+unsigned int total_go, total_ro;
+
+struct pool **pools;
+static struct pool *currentpool = NULL;
+
+int total_pools, enabled_pools;
+enum pool_strategy pool_strategy = POOL_FAILOVER;
+int opt_rotate_period;
+#ifdef USE_XTRANONCE
+static int total_urls, total_users, total_passes, total_userpasses, total_extranonce;
+#else
+static int total_urls, total_users, total_passes, total_userpasses;
+#endif
+
+static
+#ifndef HAVE_CURSES
+const
+#endif
+bool curses_active;
+
+/* Protected by ch_lock */
+char current_hash[68];
+static char prev_block[12];
+static char current_block[32];
+
+static char datestamp[40];
+static char blocktime[32];
+struct timeval block_timeval;
+static char best_share[8] = "0";
+double current_diff = 0xFFFFFFFFFFFFFFFFULL;
+static char block_diff[8];
+uint64_t best_diff = 0;
+
+struct block {
+	char hash[68];
+	UT_hash_handle hh;
+	int block_no;
+};
+
+static struct block *blocks = NULL;
+
+
+int swork_id;
+
+/* For creating a hash database of stratum shares submitted that have not had
+ * a response yet */
+struct stratum_share {
+	UT_hash_handle hh;
+	bool block;
+	struct work *work;
+	int id;
+	time_t sshare_time;
+	time_t sshare_sent;
+};
+
+static struct stratum_share *stratum_shares = NULL;
+
+char *opt_socks_proxy = NULL;
+int opt_suggest_diff;
+#if defined(USE_AVALON7) || defined (USE_AVALON8) || defined(USE_AVALON9) ||defined(USE_AVALONLC3)
+int opt_force_clean_jobs = 20;
+#endif
+static const char def_conf[] = "cgminer.conf";
+static char *default_config;
+static bool config_loaded;
+static int include_count;
+#define JSON_INCLUDE_CONF "include"
+#define JSON_LOAD_ERROR "JSON decode of file '%s' failed\n %s"
+#define JSON_LOAD_ERROR_LEN strlen(JSON_LOAD_ERROR)
+#define JSON_MAX_DEPTH 10
+#define JSON_MAX_DEPTH_ERR "Too many levels of JSON includes (limit 10) or a loop"
+#define JSON_WEB_ERROR "WEB config err"
+
+#if defined(unix) || defined(__APPLE__)
+	static char *opt_stderr_cmd = NULL;
+	static int forkpid;
+#endif // defined(unix)
+
+struct sigaction termhandler, inthandler, abrthandler;
+
+struct thread_q *getq;
+
+static uint32_t total_work;
+struct work *staged_work = NULL;
+
+struct schedtime {
+	bool enable;
+	struct tm tm;
+};
+
+struct schedtime schedstart;
+struct schedtime schedstop;
+bool sched_paused;
+
+static bool time_before(struct tm *tm1, struct tm *tm2)
+{
+	if (tm1->tm_hour < tm2->tm_hour)
+		return true;
+	if (tm1->tm_hour == tm2->tm_hour && tm1->tm_min < tm2->tm_min)
+		return true;
+	return false;
+}
+
+static bool should_run(void)
+{
+	struct timeval tv;
+	struct tm *tm;
+
+	if (!schedstart.enable && !schedstop.enable)
+		return true;
+
+	cgtime(&tv);
+	const time_t tmp_time = tv.tv_sec;
+	tm = localtime(&tmp_time);
+	if (schedstart.enable) {
+		if (!schedstop.enable) {
+			if (time_before(tm, &schedstart.tm))
+				return false;
+
+			/* This is a once off event with no stop time set */
+			schedstart.enable = false;
+			return true;
+		}
+		if (time_before(&schedstart.tm, &schedstop.tm)) {
+			if (time_before(tm, &schedstop.tm) && !time_before(tm, &schedstart.tm))
+				return true;
+			return false;
+		} /* Times are reversed */
+		if (time_before(tm, &schedstart.tm)) {
+			if (time_before(tm, &schedstop.tm))
+				return true;
+			return false;
+		}
+		return true;
+	}
+	/* only schedstop.enable == true */
+	if (!time_before(tm, &schedstop.tm))
+		return false;
+	return true;
+}
+
+void get_datestamp(char *f, size_t fsiz, struct timeval *tv)
+{
+	struct tm *tm;
+
+	const time_t tmp_time = tv->tv_sec;
+	int ms = (int)(tv->tv_usec / 1000);
+	tm = localtime(&tmp_time);
+	snprintf(f, fsiz, "[%d-%02d-%02d %02d:%02d:%02d.%03d]",
+		tm->tm_year + 1900,
+		tm->tm_mon + 1,
+		tm->tm_mday,
+		tm->tm_hour,
+		tm->tm_min,
+		tm->tm_sec, ms);
+}
+
+static void get_timestamp(char *f, size_t fsiz, struct timeval *tv)
+{
+	struct tm *tm;
+
+	const time_t tmp_time = tv->tv_sec;
+	int ms = (int)(tv->tv_usec / 1000);
+	tm = localtime(&tmp_time);
+	snprintf(f, fsiz, "[%02d:%02d:%02d.%03d]",
+		tm->tm_hour,
+		tm->tm_min,
+		tm->tm_sec, ms);
+}
+
+static char exit_buf[512];
+
+static void applog_and_exit(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsnprintf(exit_buf, sizeof(exit_buf), fmt, ap);
+	va_end(ap);
+	_applog(LOG_ERR, exit_buf, true);
+	exit(1);
+}
+
+static pthread_mutex_t sharelog_lock;
+static FILE *sharelog_file = NULL;
+
+static struct thr_info *__get_thread(int thr_id)
+{
+	return mining_thr[thr_id];
+}
+
+struct thr_info *get_thread(int thr_id)
+{
+	struct thr_info *thr;
+
+	rd_lock(&mining_thr_lock);
+	thr = __get_thread(thr_id);
+	rd_unlock(&mining_thr_lock);
+
+	return thr;
+}
+
+static struct cgpu_info *get_thr_cgpu(int thr_id)
+{
+	struct thr_info *thr = get_thread(thr_id);
+
+	return thr->cgpu;
+}
+
+struct cgpu_info *get_devices(int id)
+{
+	struct cgpu_info *cgpu;
+
+	rd_lock(&devices_lock);
+	cgpu = devices[id];
+	rd_unlock(&devices_lock);
+
+	return cgpu;
+}
+
+static void sharelog(const char*disposition, const struct work*work)
+{
+	char *target, *hash, *data;
+	struct cgpu_info *cgpu;
+	unsigned long int t;
+	struct pool *pool;
+	int thr_id, rv;
+	char s[1024];
+	size_t ret;
+
+	if (!sharelog_file)
+		return;
+
+	thr_id = work->thr_id;
+	cgpu = get_thr_cgpu(thr_id);
+	pool = work->pool;
+	t = (unsigned long int)(work->tv_work_found.tv_sec);
+	target = bin2hex(work->target, sizeof(work->target));
+	hash = bin2hex(work->hash, sizeof(work->hash));
+	data = bin2hex(work->data, sizeof(work->data));
+
+	// timestamp,disposition,target,pool,dev,thr,sharehash,sharedata
+	rv = snprintf(s, sizeof(s), "%lu,%s,%s,%s,%s%u,%u,%s,%s\n", t, disposition, target, pool->rpc_url, cgpu->drv->name, cgpu->device_id, thr_id, hash, data);
+	free(target);
+	free(hash);
+	free(data);
+	if (rv >= (int)(sizeof(s)))
+		s[sizeof(s) - 1] = '\0';
+	else if (rv < 0) {
+		applog(LOG_ERR, "sharelog printf error");
+		return;
+	}
+
+	mutex_lock(&sharelog_lock);
+	ret = fwrite(s, rv, 1, sharelog_file);
+	fflush(sharelog_file);
+	mutex_unlock(&sharelog_lock);
+
+	if (ret != 1)
+		applog(LOG_ERR, "sharelog fwrite error");
+}
+
+static char *gbt_req = "{\"id\": 0, \"method\": \"getblocktemplate\", \"params\": [{\"capabilities\": [\"coinbasetxn\", \"workid\", \"coinbase/append\"]}]}\n";
+
+static char *gbt_solo_req = "{\"id\": 0, \"method\": \"getblocktemplate\", \"params\": [{\"rules\" : [\"segwit\"]}]}\n";
+
+static const char *gbt_understood_rules[1] = { NULL };
+static const char *gbt_solo_understood_rules[2] = {"segwit", NULL};
+
+static bool gbt_check_required_rule(const char* rule, const char** understood_rules)
+{
+	const char *understood_rule;
+
+	if (!understood_rules || !rule)
+		return false;
+	while ((understood_rule = *understood_rules++)) {
+		if (strcmp(understood_rule, rule) == 0)
+			return true;
+	}
+	return false;
+}
+
+static bool gbt_check_rules(json_t* rules_arr, const char** understood_rules)
+{
+	int i, rule_count;
+	const char *rule;
+
+	if (!rules_arr)
+		return true;
+	rule_count = json_array_size(rules_arr);
+	for (i = 0; i < rule_count; i++) {
+		rule = json_string_value(json_array_get(rules_arr, i));
+		if (rule && *rule++ == '!' && !gbt_check_required_rule(rule, understood_rules))
+			return false;
+	}
+	return true;
+}
+
+/* Adjust all the pools' quota to the greatest common denominator after a pool
+ * has been added or the quotas changed. */
+void adjust_quota_gcd(void)
+{
+	unsigned long gcd, lowest_quota = ~0UL, quota;
+	struct pool *pool;
+	int i;
+
+	for (i = 0; i < total_pools; i++) {
+		pool = pools[i];
+		quota = pool->quota;
+		if (!quota)
+			continue;
+		if (quota < lowest_quota)
+			lowest_quota = quota;
+	}
+
+	if (likely(lowest_quota < ~0UL)) {
+		gcd = lowest_quota;
+		for (i = 0; i < total_pools; i++) {
+			pool = pools[i];
+			quota = pool->quota;
+			if (!quota)
+				continue;
+			while (quota % gcd)
+				gcd--;
+		}
+	} else
+		gcd = 1;
+
+	for (i = 0; i < total_pools; i++) {
+		pool = pools[i];
+		pool->quota_used *= global_quota_gcd;
+		pool->quota_used /= gcd;
+		pool->quota_gcd = pool->quota / gcd;
+	}
+
+	global_quota_gcd = gcd;
+	applog(LOG_DEBUG, "Global quota greatest common denominator set to %lu", gcd);
+}
+
+/* Return value is ignored if not called from input_pool */
+struct pool *add_pool(void)
+{
+	struct pool *pool;
+
+	pool = cgcalloc(sizeof(struct pool), 1);
+	pool->pool_no = pool->prio = total_pools;
+	pools = cgrealloc(pools, sizeof(struct pool *) * (total_pools + 2));
+	pools[total_pools++] = pool;
+	mutex_init(&pool->pool_lock);
+	if (unlikely(pthread_cond_init(&pool->cr_cond, NULL)))
+		quit(1, "Failed to pthread_cond_init in add_pool");
+	cglock_init(&pool->data_lock);
+	mutex_init(&pool->stratum_lock);
+	cglock_init(&pool->gbt_lock);
+	INIT_LIST_HEAD(&pool->curlring);
+
+	/* Make sure the pool doesn't think we've been idle since time 0 */
+	pool->tv_idle.tv_sec = ~0UL;
+
+	pool->rpc_req = gbt_req;
+	pool->rpc_proxy = NULL;
+	pool->quota = 1;
+	adjust_quota_gcd();
+#ifdef USE_XTRANONCE
+	pool->extranonce_subscribe = false;
+#endif
+	return pool;
+}
+
+/* Pool variant of test and set */
+static bool pool_tset(struct pool *pool, bool *var)
+{
+	bool ret;
+
+	mutex_lock(&pool->pool_lock);
+	ret = *var;
+	*var = true;
+	mutex_unlock(&pool->pool_lock);
+
+	return ret;
+}
+
+bool pool_tclear(struct pool *pool, bool *var)
+{
+	bool ret;
+
+	mutex_lock(&pool->pool_lock);
+	ret = *var;
+	*var = false;
+	mutex_unlock(&pool->pool_lock);
+
+	return ret;
+}
+
+struct pool *current_pool(void)
+{
+	struct pool *pool;
+
+	cg_rlock(&control_lock);
+	pool = currentpool;
+	cg_runlock(&control_lock);
+
+	return pool;
+}
+
+char *set_int_range(const char *arg, int *i, int min, int max)
+{
+	char *err = opt_set_intval(arg, i);
+
+	if (err)
+		return err;
+
+	if (*i < min || *i > max)
+		return "Value out of range";
+
+	return NULL;
+}
+
+static char *set_int_0_to_65535(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 65535);
+}
+
+static char *set_int_0_to_9999(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 9999);
+}
+
+static char *set_int_1_to_65535(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 1, 65535);
+}
+
+#if defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_AVALON9) || defined (USE_AVALONLC3)
+static char *set_int_0_to_1(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 1);
+}
+
+static char *set_int_0_to_7(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 7);
+}
+
+static char *set_int_0_to_32767(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 32767);
+}
+#endif
+
+static char *set_int_0_to_5(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 5);
+}
+
+static char *set_int_0_to_10(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 10);
+}
+
+#ifdef USE_DRAGONMINT_T1
+static char *set_int_voltage(const char *arg, int *i)
+{
+	return set_int_range(arg, i, CHIP_VOLT_MIN, CHIP_VOLT_MAX);
+}
+
+/* Intentionally does NOT accept zero so that zero means the value is NOT set
+ * and has no effect. */
+static char *set_int_1_to_31(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 1, 31);
+}
+#endif
+
+static char *set_int_0_to_100(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 100);
+}
+
+static char *set_int_0_to_255(const char *arg, int *i)
+{
+        return set_int_range(arg, i, 0, 255);
+}
+
+static char *set_int_1_to_255(const char *arg, int *i)
+{
+        return set_int_range(arg, i, 1, 255);
+}
+
+static char *set_int_0_to_7680(const char *arg, int *i)
+{
+        return set_int_range(arg, i, 0, 7680);
+}
+
+#if defined(USE_AVALON4)
+static char *set_int_1_to_60(const char *arg, int *i)
+{
+        return set_int_range(arg, i, 1, 60);
+}
+#endif
+
+static char *set_int_0_to_200(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 200);
+}
+
+static char *set_int_32_to_63(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 32, 63);
+}
+
+static char *set_int_22_to_75(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 22, 75);
+}
+
+static char *set_int_42_to_85(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 42, 85);
+}
+
+static char *set_int_1_to_10(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 1, 10);
+}
+
+static char *set_int_24_to_32(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 24, 32);
+}
+
+static char __maybe_unused *set_int_0_to_2(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 2);
+}
+
+static char __maybe_unused *set_int_0_to_3(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 3);
+}
+
+static char __maybe_unused *set_int_0_to_4(const char *arg, int *i)
+{
+	return set_int_range(arg, i, 0, 4);
+}
+
+#ifdef USE_FPGA_SERIAL
+static char *opt_add_serial;
+static char *add_serial(char *arg)
+{
+	string_elist_add(arg, &scan_devices);
+	return NULL;
+}
+#endif
+
+void get_intrange(char *arg, int *val1, int *val2)
+{
+	if (sscanf(arg, "%d-%d", val1, val2) == 1)
+		*val2 = *val1;
+}
+
+static char *set_balance(enum pool_strategy *strategy)
+{
+	*strategy = POOL_BALANCE;
+	return NULL;
+}
+
+static char *set_loadbalance(enum pool_strategy *strategy)
+{
+	*strategy = POOL_LOADBALANCE;
+	return NULL;
+}
+
+static char *set_rotate(const char *arg, char __maybe_unused *i)
+{
+	pool_strategy = POOL_ROTATE;
+	return set_int_range(arg, &opt_rotate_period, 0, 9999);
+}
+
+static char *set_rr(enum pool_strategy *strategy)
+{
+	*strategy = POOL_ROUNDROBIN;
+	return NULL;
+}
+
+/* Detect that url is for a stratum protocol either via the presence of
+ * stratum+tcp or by detecting a stratum server response */
+bool detect_stratum(struct pool *pool, char *url)
+{
+	bool ret = false;
+
+	if (!extract_sockaddr(url, &pool->sockaddr_url, &pool->stratum_port))
+		goto out;
+
+	if (!strncasecmp(url, "stratum+tcp://", 14)) {
+		pool->rpc_url = strdup(url);
+		pool->has_stratum = true;
+		pool->stratum_url = pool->sockaddr_url;
+		ret = true;
+	}
+out:
+	if (!ret) {
+		free(pool->sockaddr_url);
+		free(pool->stratum_port);
+		pool->stratum_port = pool->sockaddr_url = NULL;
+	}
+	return ret;
+}
+
+static struct pool *add_url(void)
+{
+	total_urls++;
+	if (total_urls > total_pools)
+		add_pool();
+	return pools[total_urls - 1];
+}
+
+static char *setup_url(struct pool *pool, char *arg)
+{
+	arg = get_proxy(arg, pool);
+
+	if (detect_stratum(pool, arg))
+		goto out;
+
+	opt_set_charp(arg, &pool->rpc_url);
+	if (strncmp(arg, "http://", 7) &&
+	    strncmp(arg, "https://", 8)) {
+		char httpinput[256];
+
+		strcpy(httpinput, "stratum+tcp://");
+		strncat(httpinput, arg, 242);
+		detect_stratum(pool, httpinput);
+	}
+out:
+	return pool->rpc_url;
+}
+
+static char *set_url(char *arg)
+{
+	struct pool *pool = add_url();
+
+	setup_url(pool, arg);
+#ifdef USE_XTRANONCE
+	if (strstr(pool->rpc_url, ".nicehash.com") || strstr(pool->rpc_url, "#xnsub")) {
+		pool->extranonce_subscribe = true;
+		applog(LOG_DEBUG, "Pool %d extranonce subscribing enabled.", pool->pool_no);
+	}
+#endif
+	return NULL;
+}
+
+static char *set_quota(char *arg)
+{
+	char *semicolon = strchr(arg, ';'), *url;
+	int len, qlen, quota;
+	struct pool *pool;
+
+	if (!semicolon)
+		return "No semicolon separated quota;URL pair found";
+	len = strlen(arg);
+	*semicolon = '\0';
+	qlen = strlen(arg);
+	if (!qlen)
+		return "No parameter for quota found";
+	len -= qlen + 1;
+	if (len < 1)
+		return "No parameter for URL found";
+	quota = atoi(arg);
+	if (quota < 0)
+		return "Invalid negative parameter for quota set";
+	url = arg + qlen + 1;
+	pool = add_url();
+	setup_url(pool, url);
+	pool->quota = quota;
+	applog(LOG_INFO, "Setting pool %d to quota %d", pool->pool_no, pool->quota);
+	adjust_quota_gcd();
+
+	return NULL;
+}
+
+static char *set_user(const char *arg)
+{
+	struct pool *pool;
+
+	if (total_userpasses)
+		return "Use only user + pass or userpass, but not both";
+	total_users++;
+	if (total_users > total_pools)
+		add_pool();
+
+	pool = pools[total_users - 1];
+	opt_set_charp(arg, &pool->rpc_user);
+
+	return NULL;
+}
+
+static char *set_pass(const char *arg)
+{
+	struct pool *pool;
+
+	if (total_userpasses)
+		return "Use only user + pass or userpass, but not both";
+	total_passes++;
+	if (total_passes > total_pools)
+		add_pool();
+
+	pool = pools[total_passes - 1];
+	opt_set_charp(arg, &pool->rpc_pass);
+
+	return NULL;
+}
+
+static char *set_userpass(const char *arg)
+{
+	struct pool *pool;
+	char *updup;
+
+	if (total_users || total_passes)
+		return "Use only user + pass or userpass, but not both";
+	total_userpasses++;
+	if (total_userpasses > total_pools)
+		add_pool();
+
+	pool = pools[total_userpasses - 1];
+	updup = strdup(arg);
+	opt_set_charp(arg, &pool->rpc_userpass);
+	pool->rpc_user = strtok(updup, ":");
+	if (!pool->rpc_user)
+		return "Failed to find : delimited user info";
+	pool->rpc_pass = strtok(NULL, ":");
+	if (!pool->rpc_pass)
+		pool->rpc_pass = strdup("");
+
+	return NULL;
+}
+#ifdef USE_XTRANONCE
+static char *set_extranonce_subscribe(char *arg)
+{
+	struct pool *pool;
+
+	total_extranonce++;
+	if (total_extranonce > total_pools)
+		add_pool();
+
+	pool = pools[total_extranonce - 1];
+	applog(LOG_DEBUG, "Enable extranonce subscribe on %d", pool->pool_no);
+	opt_set_bool(&pool->extranonce_subscribe);
+
+	return NULL;
+}
+#endif
+static char *enable_debug(bool *flag)
+{
+	*flag = true;
+	/* Turn on verbose output, too. */
+	opt_log_output = true;
+	return NULL;
+}
+
+static char *opt_set_sched_start;
+static char *opt_set_sched_stop;
+
+static char *set_schedtime(const char *arg, struct schedtime *st)
+{
+	if (sscanf(arg, "%d:%d", &st->tm.tm_hour, &st->tm.tm_min) != 2)
+		return "Invalid time set, should be HH:MM";
+	if (st->tm.tm_hour > 23 || st->tm.tm_min > 59 || st->tm.tm_hour < 0 || st->tm.tm_min < 0)
+		return "Invalid time set.";
+	st->enable = true;
+	return NULL;
+}
+
+static char *set_sched_start(const char *arg)
+{
+	return set_schedtime(arg, &schedstart);
+}
+
+static char *set_sched_stop(const char *arg)
+{
+	return set_schedtime(arg, &schedstop);
+}
+
+static char *opt_set_sharelog;
+static char* set_sharelog(char *arg)
+{
+	char *r = "";
+	long int i = strtol(arg, &r, 10);
+
+	if ((!*r) && i >= 0 && i <= INT_MAX) {
+		sharelog_file = fdopen((int)i, "a");
+		if (!sharelog_file)
+			applog(LOG_ERR, "Failed to open fd %u for share log", (unsigned int)i);
+	} else if (!strcmp(arg, "-")) {
+		sharelog_file = stdout;
+		if (!sharelog_file)
+			applog(LOG_ERR, "Standard output missing for share log");
+	} else {
+		sharelog_file = fopen(arg, "a");
+		if (!sharelog_file)
+			applog(LOG_ERR, "Failed to open %s for share log", arg);
+	}
+
+	return NULL;
+}
+
+static char *temp_cutoff_str = NULL;
+static char __maybe_unused *opt_set_temp_cutoff;
+
+char *set_temp_cutoff(char *arg)
+{
+	int val;
+
+	if (!(arg && arg[0]))
+		return "Invalid parameters for set temp cutoff";
+	val = atoi(arg);
+	if (val < 0 || val > 200)
+		return "Invalid value passed to set temp cutoff";
+	temp_cutoff_str = arg;
+
+	return NULL;
+}
+
+static void load_temp_cutoffs()
+{
+	int i, val = 0, device = 0;
+	char *nextptr;
+
+	if (temp_cutoff_str) {
+		for (device = 0, nextptr = strtok(temp_cutoff_str, ","); nextptr; ++device, nextptr = strtok(NULL, ",")) {
+			if (device >= total_devices)
+				quit(1, "Too many values passed to set temp cutoff");
+			val = atoi(nextptr);
+			if (val < 0 || val > 200)
+				quit(1, "Invalid value passed to set temp cutoff");
+
+			rd_lock(&devices_lock);
+			devices[device]->cutofftemp = val;
+			rd_unlock(&devices_lock);
+		}
+	} else {
+		rd_lock(&devices_lock);
+		for (i = device; i < total_devices; ++i) {
+			if (!devices[i]->cutofftemp)
+				devices[i]->cutofftemp = opt_cutofftemp;
+		}
+		rd_unlock(&devices_lock);
+
+		return;
+	}
+	if (device <= 1) {
+		rd_lock(&devices_lock);
+		for (i = device; i < total_devices; ++i)
+			devices[i]->cutofftemp = val;
+		rd_unlock(&devices_lock);
+	}
+}
+
+static char *set_float_0_to_500(const char *arg, float *i)
+{
+	char *err = opt_set_floatval(arg, i);
+
+	if (err)
+		return err;
+
+	if (*i < 0 || *i > 500)
+		return "Value out of range";
+
+	return NULL;
+}
+
+static char *set_float_100_to_500(const char *arg, float *i)
+{
+	char *err = opt_set_floatval(arg, i);
+
+	if (err)
+		return err;
+
+	if (*i < 100 || *i > 500)
+		return "Value out of range";
+
+	return NULL;
+}
+
+static char *set_float_125_to_500(const char *arg, float *i)
+{
+	char *err = opt_set_floatval(arg, i);
+
+	if (err)
+		return err;
+
+	if (*i < 125 || *i > 500)
+		return "Value out of range";
+
+	return NULL;
+}
+
+static char *set_float_100_to_250(const char *arg, float *i)
+{
+	char *err = opt_set_floatval(arg, i);
+
+	if (err)
+		return err;
+
+	if (*i < 100 || *i > 250)
+		return "Value out of range";
+
+	return NULL;
+}
+
+static char *set_null(const char __maybe_unused *arg)
+{
+	return NULL;
+}
+
+#ifdef USE_BITMAIN_SOC
+static char *set_version_path(const char *arg)
+{
+    opt_set_charp(arg, &opt_version_path);
+
+    return NULL;
+}
+#endif
+
+/* These options are available from config file or commandline */
+static struct opt_table opt_config_table[] = {
+#ifdef USE_ICARUS
+	OPT_WITH_ARG("--anu-freq",
+		     set_float_125_to_500, &opt_show_floatval, &opt_anu_freq,
+		     "Set AntminerU1/2 frequency in MHz, range 125-500"),
+#endif
+	OPT_WITH_ARG("--api-allow",
+		     opt_set_charp, NULL, &opt_api_allow,
+		     "Allow API access only to the given list of [G:]IP[/Prefix] addresses[/subnets]"),
+	OPT_WITH_ARG("--api-description",
+		     opt_set_charp, NULL, &opt_api_description,
+		     "Description placed in the API status header, default: cgminer version"),
+	OPT_WITH_ARG("--api-groups",
+		     opt_set_charp, NULL, &opt_api_groups,
+		     "API one letter groups G:cmd:cmd[,P:cmd:*...] defining the cmds a groups can use"),
+	OPT_WITHOUT_ARG("--api-listen",
+			opt_set_bool, &opt_api_listen,
+			"Enable API, default: disabled"),
+	OPT_WITHOUT_ARG("--api-mcast",
+			opt_set_bool, &opt_api_mcast,
+			"Enable API Multicast listener, default: disabled"),
+	OPT_WITH_ARG("--api-mcast-addr",
+		     opt_set_charp, NULL, &opt_api_mcast_addr,
+		     "API Multicast listen address"),
+	OPT_WITH_ARG("--api-mcast-code",
+		     opt_set_charp, NULL, &opt_api_mcast_code,
+		     "Code expected in the API Multicast message, don't use '-'"),
+	OPT_WITH_ARG("--api-mcast-des",
+		     opt_set_charp, NULL, &opt_api_mcast_des,
+		     "Description appended to the API Multicast reply, default: ''"),
+	OPT_WITH_ARG("--api-mcast-port",
+		     set_int_1_to_65535, opt_show_intval, &opt_api_mcast_port,
+		     "API Multicast listen port"),
+	OPT_WITHOUT_ARG("--api-network",
+			opt_set_bool, &opt_api_network,
+			"Allow API (if enabled) to listen on/for any address, default: only 127.0.0.1"),
+	OPT_WITH_ARG("--api-port",
+		     set_int_1_to_65535, opt_show_intval, &opt_api_port,
+		     "Port number of miner API"),
+	OPT_WITH_ARG("--api-host",
+		     opt_set_charp, NULL, &opt_api_host,
+		     "Specify API listen address, default: 0.0.0.0"),
+#ifdef USE_ICARUS
+	OPT_WITH_ARG("--au3-freq",
+		     set_float_100_to_250, &opt_show_floatval, &opt_au3_freq,
+		     "Set AntminerU3 frequency in MHz, range 100-250"),
+	OPT_WITH_ARG("--au3-volt",
+		     set_int_0_to_9999, &opt_show_intval, &opt_au3_volt,
+		     "Set AntminerU3 voltage in mv, range 725-850, 0 to not set"),
+#endif
+#ifdef USE_AVALON
+	OPT_WITHOUT_ARG("--avalon-auto",
+			opt_set_bool, &opt_avalon_auto,
+			"Adjust avalon overclock frequency dynamically for best hashrate"),
+	OPT_WITH_ARG("--avalon-cutoff",
+		     set_int_0_to_100, opt_show_intval, &opt_avalon_overheat,
+		     "Set avalon overheat cut off temperature"),
+	OPT_WITH_CBARG("--avalon-fan",
+		     set_avalon_fan, NULL, &opt_set_avalon_fan,
+		     "Set fanspeed percentage for avalon, single value or range (default: 20-100)"),
+	OPT_WITH_CBARG("--avalon-freq",
+		     set_avalon_freq, NULL, &opt_set_avalon_freq,
+		     "Set frequency range for avalon-auto, single value or range"),
+	OPT_WITH_ARG("--avalon-options",
+		     opt_set_charp, NULL, &opt_avalon_options,
+		     "Set avalon options baud:miners:asic:timeout:freq:tech"),
+	OPT_WITH_ARG("--avalon-temp",
+		     set_int_0_to_100, opt_show_intval, &opt_avalon_temp,
+		     "Set avalon target temperature"),
+#endif
+#ifdef USE_AVALON2
+	OPT_WITH_CBARG("--avalon2-freq",
+		     set_avalon2_freq, NULL, &opt_set_avalon2_freq,
+		     "Set frequency range for Avalon2, single value or range, step: 25"),
+	OPT_WITH_CBARG("--avalon2-voltage",
+		     set_avalon2_voltage, NULL, &opt_set_avalon2_voltage,
+		     "Set Avalon2 core voltage, in millivolts, step: 125"),
+	OPT_WITH_CBARG("--avalon2-fan",
+		     set_avalon2_fan, NULL, &opt_set_avalon2_fan,
+		     "Set Avalon2 target fan speed"),
+	OPT_WITH_ARG("--avalon2-cutoff",
+		     set_int_0_to_100, opt_show_intval, &opt_avalon2_overheat,
+		     "Set Avalon2 overheat cut off temperature"),
+	OPT_WITHOUT_ARG("--avalon2-fixed-speed",
+		     set_avalon2_fixed_speed, &opt_avalon2_fan_fixed,
+		     "Set Avalon2 fan to fixed speed"),
+	OPT_WITH_ARG("--avalon2-polling-delay",
+		     set_int_1_to_65535, opt_show_intval, &opt_avalon2_polling_delay,
+		     "Set Avalon2 polling delay value (ms)"),
+#endif
+#ifdef USE_AVALON4
+	OPT_WITHOUT_ARG("--avalon4-automatic-voltage",
+		     opt_set_bool, &opt_avalon4_autov,
+		     "Automatic adjust voltage base on module DH"),
+	OPT_WITH_CBARG("--avalon4-voltage",
+		     set_avalon4_voltage, NULL, &opt_set_avalon4_voltage,
+		     "Set Avalon4 core voltage, in millivolts, step: 125"),
+	OPT_WITH_CBARG("--avalon4-freq",
+		     set_avalon4_freq, NULL, &opt_set_avalon4_freq,
+		     "Set frequency for Avalon4, 1 to 3 values, example: 445:385:370"),
+	OPT_WITH_CBARG("--avalon4-fan",
+		     set_avalon4_fan, NULL, &opt_set_avalon4_fan,
+		     "Set Avalon4 target fan speed range"),
+	OPT_WITH_ARG("--avalon4-temp",
+		     set_int_22_to_75, opt_show_intval, &opt_avalon4_temp_target,
+		     "Set Avalon4 target temperature"),
+	OPT_WITH_ARG("--avalon4-cutoff",
+		     set_int_42_to_85, opt_show_intval, &opt_avalon4_overheat,
+		     "Set Avalon4 overheat cut off temperature"),
+	OPT_WITH_ARG("--avalon4-polling-delay",
+		     set_int_1_to_65535, opt_show_intval, &opt_avalon4_polling_delay,
+		     "Set Avalon4 polling delay value (ms)"),
+	OPT_WITH_ARG("--avalon4-ntime-offset",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_ntime_offset,
+		     "Set Avalon4 MM ntime rolling max offset"),
+	OPT_WITH_ARG("--avalon4-aucspeed",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_aucspeed,
+		     "Set Avalon4 AUC IIC bus speed"),
+	OPT_WITH_ARG("--avalon4-aucxdelay",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_aucxdelay,
+		     "Set Avalon4 AUC IIC xfer read delay, 4800 ~= 1ms"),
+	OPT_WITH_ARG("--avalon4-miningmode",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_miningmode,
+		     "Set Avalon4 mining mode(0:custom, 1:eco, 2:normal, 3:turbo"),
+	OPT_WITHOUT_ARG("--avalon4-freezesafe",
+		     opt_set_bool, &opt_avalon4_freezesafe,
+		     "Make Avalon4 running as a radiator when stratum server failed"),
+	OPT_WITH_ARG("--avalon4-ntcb",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_ntcb,
+		     "Set Avalon4 MM NTC B value"),
+	OPT_WITH_ARG("--avalon4-freq-min",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_freq_min,
+		     "Set minimum frequency for Avalon4"),
+	OPT_WITH_ARG("--avalon4-freq-max",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_freq_max,
+		     "Set maximum frequency for Avalon4"),
+	OPT_WITHOUT_ARG("--avalon4-noncecheck-off",
+		     opt_set_invbool, &opt_avalon4_noncecheck,
+		     "Disable A3218 inside nonce check function"),
+	OPT_WITH_ARG("--avalon4-smart-speed",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_smart_speed,
+		     "Set smart speed, range 0-3. 0 means Disable"),
+	OPT_WITH_ARG("--avalon4-speed-bingo",
+		     set_int_1_to_255, opt_show_intval, &opt_avalon4_speed_bingo,
+		     "Set A3218 speed bingo for smart speed mode 1"),
+	OPT_WITH_ARG("--avalon4-speed-error",
+		     set_int_1_to_255, opt_show_intval, &opt_avalon4_speed_error,
+		     "Set A3218 speed error for smart speed mode 1"),
+	OPT_WITH_ARG("--avalon4-least-pll",
+		     set_int_0_to_7680, opt_show_intval, &opt_avalon4_least_pll_check,
+		     "Set least pll check threshold for smart speed mode 2"),
+	OPT_WITH_ARG("--avalon4-most-pll",
+		     set_int_0_to_7680, opt_show_intval, &opt_avalon4_most_pll_check,
+		     "Set most pll check threshold for smart speed mode 2"),
+	OPT_WITHOUT_ARG("--avalon4-iic-detect",
+		     opt_set_bool, &opt_avalon4_iic_detect,
+		     "Enable miner detect through iic controller"),
+	OPT_WITH_ARG("--avalon4-freqadj-time",
+		     set_int_1_to_60, opt_show_intval, &opt_avalon4_freqadj_time,
+		     "Set Avalon4 check interval when run in AVA4_FREQ_TEMPADJ_MODE"),
+	OPT_WITH_ARG("--avalon4-delta-temp",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_delta_temp,
+		     "Set Avalon4 delta temperature when reset freq in AVA4_FREQ_TEMPADJ_MODE"),
+	OPT_WITH_ARG("--avalon4-delta-freq",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_delta_freq,
+		     "Set Avalon4 delta freq when adjust freq in AVA4_FREQ_TEMPADJ_MODE"),
+	OPT_WITH_ARG("--avalon4-freqadj-temp",
+		     opt_set_intval, opt_show_intval, &opt_avalon4_freqadj_temp,
+		     "Set Avalon4 check temperature when run into AVA4_FREQ_TEMPADJ_MODE"),
+#endif
+#ifdef USE_AVALON7
+	OPT_WITH_CBARG("--avalon7-voltage",
+		     set_avalon7_voltage, NULL, &opt_set_avalon7_voltage,
+		     "Set Avalon7 default core voltage, in millivolts, step: 78"),
+	OPT_WITH_CBARG("--avalon7-voltage-level",
+		     set_avalon7_voltage_level, NULL, &opt_set_avalon7_voltage_level,
+		     "Set Avalon7 default level of core voltage, range:[0, 15], step: 1"),
+	OPT_WITH_CBARG("--avalon7-voltage-offset",
+		     set_avalon7_voltage_offset, NULL, &opt_set_avalon7_voltage_offset,
+		     "Set Avalon7 default offset of core voltage, range:[-2, 1], step: 1"),
+	OPT_WITH_CBARG("--avalon7-freq",
+		     set_avalon7_freq, NULL, &opt_set_avalon7_freq,
+		     "Set Avalon7 default frequency, range:[24, 1404], step: 12, example: 500"),
+	OPT_WITH_ARG("--avalon7-freq-sel",
+		     set_int_0_to_5, opt_show_intval, &opt_avalon7_freq_sel,
+		     "Set Avalon7 default frequency select, range:[0, 5], step: 1, example: 3"),
+	OPT_WITH_CBARG("--avalon7-fan",
+		     set_avalon7_fan, NULL, &opt_set_avalon7_fan,
+		     "Set Avalon7 target fan speed, range:[0, 100], step: 1, example: 0-100"),
+	OPT_WITH_ARG("--avalon7-temp",
+		     set_int_0_to_100, opt_show_intval, &opt_avalon7_temp_target,
+		     "Set Avalon7 target temperature, range:[0, 100]"),
+	OPT_WITH_ARG("--avalon7-polling-delay",
+		     set_int_1_to_65535, opt_show_intval, &opt_avalon7_polling_delay,
+		     "Set Avalon7 polling delay value (ms)"),
+	OPT_WITH_ARG("--avalon7-aucspeed",
+		     opt_set_intval, opt_show_intval, &opt_avalon7_aucspeed,
+		     "Set AUC3 IIC bus speed"),
+	OPT_WITH_ARG("--avalon7-aucxdelay",
+		     opt_set_intval, opt_show_intval, &opt_avalon7_aucxdelay,
+		     "Set AUC3 IIC xfer read delay, 4800 ~= 1ms"),
+	OPT_WITH_ARG("--avalon7-smart-speed",
+		     opt_set_intval, opt_show_intval, &opt_avalon7_smart_speed,
+		     "Set Avalon7 smart speed, range 0-1. 0 means Disable"),
+	OPT_WITH_ARG("--avalon7-th-pass",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon7_th_pass,
+		     "Set A3212 th pass value"),
+	OPT_WITH_ARG("--avalon7-th-fail",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon7_th_fail,
+		     "Set A3212 th fail value"),
+	OPT_WITH_ARG("--avalon7-th-init",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon7_th_init,
+		     "Set A3212 th init value"),
+	OPT_WITH_ARG("--avalon7-th-ms",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon7_th_ms,
+		     "Set A3212 th ms value"),
+	OPT_WITH_ARG("--avalon7-th-timeout",
+		     opt_set_uintval, opt_show_uintval, &opt_avalon7_th_timeout,
+		     "Set A3212 th timeout value"),
+	OPT_WITHOUT_ARG("--avalon7-iic-detect",
+		     opt_set_bool, &opt_avalon7_iic_detect,
+		     "Enable Avalon7 detect through iic controller"),
+	OPT_WITH_ARG("--avalon7-nonce-mask",
+		     set_int_24_to_32, opt_show_intval, &opt_avalon7_nonce_mask,
+		     "Set A3212 nonce mask, range 24-32."),
+	OPT_WITHOUT_ARG("--no-avalon7-asic-debug",
+		     opt_set_invbool, &opt_avalon7_asic_debug,
+		     "Disable A3212 debug."),
+	OPT_WITHOUT_ARG("--avalon7-ssplus-enable",
+		     opt_set_bool, &opt_avalon7_ssplus_enable,
+		     "Enable avalon7 smart speed plus."),
+#endif
+#ifdef USE_AVALON8
+	OPT_WITH_CBARG("--avalon8-voltage-level",
+		     set_avalon8_voltage_level, NULL, &opt_set_avalon8_voltage_level,
+		     "Set Avalon8 default level of core voltage, range:[0, 15], step: 1"),
+	OPT_WITH_CBARG("--avalon8-voltage-level-offset",
+		     set_avalon8_voltage_level_offset, NULL, &opt_set_avalon8_voltage_level_offset,
+		     "Set Avalon8 default offset of core voltage level, range:[-2, 1], step: 1"),
+	OPT_WITH_CBARG("--avalon8-freq",
+		     set_avalon8_freq, NULL, &opt_set_avalon8_freq,
+		     "Set Avalon8 default frequency, range:[25, 1200], step: 25, example: 800"),
+	OPT_WITH_ARG("--avalon8-freq-sel",
+		     set_int_0_to_7, opt_show_intval, &opt_avalon8_freq_sel,
+		     "Set Avalon8 default frequency select, range:[0, 7], step: 1, example: 7"),
+	OPT_WITH_CBARG("--avalon8-fan",
+		     set_avalon8_fan, NULL, &opt_set_avalon8_fan,
+		     "Set Avalon8 target fan speed, range:[0, 100], step: 1, example: 0-100"),
+	OPT_WITH_ARG("--avalon8-temp",
+		     set_int_0_to_100, opt_show_intval, &opt_avalon8_temp_target,
+		     "Set Avalon8 target temperature, range:[0, 100]"),
+	OPT_WITH_ARG("--avalon8-polling-delay",
+		     set_int_1_to_65535, opt_show_intval, &opt_avalon8_polling_delay,
+		     "Set Avalon8 polling delay value (ms)"),
+	OPT_WITH_ARG("--avalon8-aucspeed",
+		     opt_set_intval, opt_show_intval, &opt_avalon8_aucspeed,
+		     "Set AUC3 IIC bus speed"),
+	OPT_WITH_ARG("--avalon8-aucxdelay",
+		     opt_set_intval, opt_show_intval, &opt_avalon8_aucxdelay,
+		     "Set AUC3 IIC xfer read delay, 4800 ~= 1ms"),
+	OPT_WITH_ARG("--avalon8-smart-speed",
+		     opt_set_intval, opt_show_intval, &opt_avalon8_smart_speed,
+		     "Set Avalon8 smart speed, range 0-1. 0 means Disable"),
+	OPT_WITH_ARG("--avalon8-th-pass",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon8_th_pass,
+		     "Set A3210 th pass value"),
+	OPT_WITH_ARG("--avalon8-th-fail",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon8_th_fail,
+		     "Set A3210 th fail value"),
+	OPT_WITH_ARG("--avalon8-th-init",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon8_th_init,
+		     "Set A3210 th init value"),
+	OPT_WITH_ARG("--avalon8-th-ms",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon8_th_ms,
+		     "Set A3210 th ms value"),
+	OPT_WITH_ARG("--avalon8-th-timeout",
+		     opt_set_uintval, opt_show_uintval, &opt_avalon8_th_timeout,
+		     "Set A3210 th timeout value"),
+	OPT_WITH_ARG("--avalon8-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_th_add,
+		     "Set A3210 th add value"),
+	OPT_WITH_ARG("--avalon8-th-mssel",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_th_mssel,
+		     "Set A3210 th mssel value"),
+	OPT_WITH_ARG("--avalon8-lv2-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_lv2_th_add,
+		     "Set A3210 lv2 th add value"),
+	OPT_WITH_ARG("--avalon8-lv2-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon8_lv2_th_ms,
+		     "Set A3210 lv2 th ms value"),
+	OPT_WITH_ARG("--avalon8-lv3-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_lv3_th_add,
+		     "Set A3210 lv3 th add value"),
+	OPT_WITH_ARG("--avalon8-lv3-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon8_lv3_th_ms,
+		     "Set A3210 lv3 th ms value"),
+	OPT_WITH_ARG("--avalon8-lv4-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_lv4_th_add,
+		     "Set A3210 lv4 th add value"),
+	OPT_WITH_ARG("--avalon8-lv4-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon8_lv4_th_ms,
+		     "Set A3210 lv4 th ms value"),
+	OPT_WITH_ARG("--avalon8-lv5-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_lv5_th_add,
+		     "Set A3210 lv5 th add value"),
+	OPT_WITH_ARG("--avalon8-lv5-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon8_lv5_th_ms,
+		     "Set A3210 lv5 th ms value"),
+	OPT_WITH_ARG("--avalon8-lv6-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_lv6_th_add,
+		     "Set A3210 lv6 th add value"),
+	OPT_WITH_ARG("--avalon8-lv6-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon8_lv6_th_ms,
+		     "Set A3210 lv6 th ms value"),
+	OPT_WITH_ARG("--avalon8-lv7-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_lv7_th_add,
+		     "Set A3210 lv7 th add value"),
+	OPT_WITH_ARG("--avalon8-lv7-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon8_lv7_th_ms,
+		     "Set A3210 lv7 th ms value"),
+	OPT_WITHOUT_ARG("--avalon8-iic-detect",
+		     opt_set_bool, &opt_avalon8_iic_detect,
+		     "Enable Avalon8 detect through iic controller"),
+	OPT_WITH_ARG("--avalon8-nonce-mask",
+		     set_int_24_to_32, opt_show_intval, &opt_avalon8_nonce_mask,
+		     "Set A3210 nonce mask, range 24-32."),
+	OPT_WITH_ARG("--avalon8-nonce-check",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_nonce_check,
+		     "Set A3210 nonce check, range 0-1."),
+	OPT_WITH_ARG("--avalon8-roll-enable",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_roll_enable,
+		     "Set A3210 roll enable, range 0-1."),
+	OPT_WITH_ARG("--avalon8-mux-l2h",
+		     set_int_0_to_2, opt_show_intval, &opt_avalon8_mux_l2h,
+		     "Set Avalon8 mux l2h, range 0-2."),
+	OPT_WITH_ARG("--avalon8-mux-h2l",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon8_mux_h2l,
+		     "Set Avalon8 mux h2l, range 0-1."),
+	OPT_WITH_ARG("--avalon8-h2ltime0-spd",
+		     set_int_0_to_255, opt_show_intval, &opt_avalon8_h2ltime0_spd,
+		     "Set Avalon8 h2ltime0 spd, range 0-255."),
+#endif
+#ifdef USE_AVALON9
+	OPT_WITH_CBARG("--avalon9-voltage-level",
+		     set_avalon9_voltage_level, NULL, &opt_set_avalon9_voltage_level,
+		     "Set Avalon9 default level of core voltage, range:[0, 15], step: 1"),
+	OPT_WITH_CBARG("--avalon9-voltage-level-offset",
+		     set_avalon9_voltage_level_offset, NULL, &opt_set_avalon9_voltage_level_offset,
+		     "Set Avalon9 default offset of core voltage level, range:[-2, 1], step: 1"),
+	OPT_WITH_CBARG("--avalon9-freq",
+		     set_avalon9_freq, NULL, &opt_set_avalon9_freq,
+		     "Set Avalon9 default frequency, range:[25, 1200], step: 25, example: 800"),
+	OPT_WITH_ARG("--avalon9-freq-sel",
+		     set_int_0_to_7, opt_show_intval, &opt_avalon9_freq_sel,
+		     "Set Avalon9 default frequency select, range:[0, 7], step: 1, example: 7"),
+	OPT_WITH_CBARG("--avalon9-fan",
+		     set_avalon9_fan, NULL, &opt_set_avalon9_fan,
+		     "Set Avalon9 target fan speed, range:[0, 100], step: 1, example: 0-100"),
+	OPT_WITH_ARG("--avalon9-temp",
+		     set_int_0_to_100, opt_show_intval, &opt_avalon9_temp_target,
+		     "Set Avalon9 target temperature, range:[0, 100]"),
+	OPT_WITH_ARG("--avalon9-polling-delay",
+		     set_int_1_to_65535, opt_show_intval, &opt_avalon9_polling_delay,
+		     "Set Avalon9 polling delay value (ms)"),
+	OPT_WITH_ARG("--avalon9-aucspeed",
+		     opt_set_intval, opt_show_intval, &opt_avalon9_aucspeed,
+		     "Set AUC3 IIC bus speed"),
+	OPT_WITH_ARG("--avalon9-aucxdelay",
+		     opt_set_intval, opt_show_intval, &opt_avalon9_aucxdelay,
+		     "Set AUC3 IIC xfer read delay, 4800 ~= 1ms"),
+	OPT_WITH_ARG("--avalon9-smart-speed",
+		     opt_set_intval, opt_show_intval, &opt_avalon9_smart_speed,
+		     "Set Avalon9 smart speed, range 0-1. 0 means Disable"),
+	OPT_WITH_ARG("--avalon9-th-pass",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon9_th_pass,
+		     "Set A3206 th pass value"),
+	OPT_WITH_ARG("--avalon9-th-fail",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon9_th_fail,
+		     "Set A3206 th fail value"),
+	OPT_WITH_ARG("--avalon9-th-init",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalon9_th_init,
+		     "Set A3206 th init value"),
+	OPT_WITH_ARG("--avalon9-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon9_th_ms,
+		     "Set A3206 th ms value"),
+	OPT_WITH_ARG("--avalon9-th-timeout",
+		     opt_set_uintval, opt_show_uintval, &opt_avalon9_th_timeout,
+		     "Set A3206 th timeout value"),
+	OPT_WITH_ARG("--avalon9-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_th_add,
+		     "Set A3206 th add value"),
+	OPT_WITH_ARG("--avalon9-th-mssel",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_th_mssel,
+		     "Set A3206 th mssel value"),
+	OPT_WITH_ARG("--avalon9-lv2-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_lv2_th_add,
+		     "Set A3206 lv2 th add value"),
+	OPT_WITH_ARG("--avalon9-lv2-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv2_th_ms,
+		     "Set A3206 lv2 th ms value"),
+	OPT_WITH_ARG("--avalon9-lv3-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_lv3_th_add,
+		     "Set A3206 lv3 th add value"),
+	OPT_WITH_ARG("--avalon9-lv3-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv3_th_ms,
+		     "Set A3206 lv3 th ms value"),
+	OPT_WITH_ARG("--avalon9-lv4-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_lv4_th_add,
+		     "Set A3206 lv4 th add value"),
+	OPT_WITH_ARG("--avalon9-lv4-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv4_th_ms,
+		     "Set A3206 lv4 th ms value"),
+	OPT_WITH_ARG("--avalon9-lv5-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_lv5_th_add,
+		     "Set A3206 lv5 th add value"),
+	OPT_WITH_ARG("--avalon9-lv5-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv5_th_ms,
+		     "Set A3206 lv5 th ms value"),
+	OPT_WITH_ARG("--avalon9-lv6-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_lv6_th_add,
+		     "Set A3206 lv6 th add value"),
+	OPT_WITH_ARG("--avalon9-lv6-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv6_th_ms,
+		     "Set A3206 lv6 th ms value"),
+	OPT_WITH_ARG("--avalon9-lv7-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_lv7_th_add,
+		     "Set A3206 lv7 th add value"),
+	OPT_WITH_ARG("--avalon9-lv7-th-ms",
+		     set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv7_th_ms,
+		     "Set A3206 lv7 th ms value"),
+	OPT_WITHOUT_ARG("--avalon9-iic-detect",
+		     opt_set_bool, &opt_avalon9_iic_detect,
+		     "Enable Avalon9 detect through iic controller"),
+	OPT_WITH_ARG("--avalon9-nonce-mask",
+		     set_int_24_to_32, opt_show_intval, &opt_avalon9_nonce_mask,
+		     "Set A3206 nonce mask, range 24-32."),
+	OPT_WITH_ARG("--avalon9-nonce-check",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_nonce_check,
+		     "Set A3206 nonce check, range 0-1."),
+	OPT_WITH_ARG("--avalon9-roll-enable",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_roll_enable,
+		     "Set A3206 roll enable, range 0-1."),
+	OPT_WITH_ARG("--avalon9-mux-l2h",
+		     set_int_0_to_2, opt_show_intval, &opt_avalon9_mux_l2h,
+		     "Set Avalon9 mux l2h, range 0-2."),
+	OPT_WITH_ARG("--avalon9-mux-h2l",
+		     set_int_0_to_1, opt_show_intval, &opt_avalon9_mux_h2l,
+		     "Set Avalon9 mux h2l, range 0-1."),
+	OPT_WITH_ARG("--avalon9-h2ltime0-spd",
+		     set_int_0_to_255, opt_show_intval, &opt_avalon9_h2ltime0_spd,
+		     "Set Avalon9 h2ltime0 spd, range 0-255."),
+	OPT_WITH_ARG("--avalon9-spdlow",
+		     set_int_0_to_7, opt_show_intval, &opt_avalon9_spdlow,
+		     "Set Avalon9 spdlow, range 0-7."),
+	OPT_WITH_ARG("--avalon9-spdhigh",
+		     set_int_0_to_7, opt_show_intval, &opt_avalon9_spdhigh,
+		     "Set Avalon9 spdhigh, range 0-7."),
+	OPT_WITH_ARG("--avalon9-tbase",
+		     set_int_0_to_255, opt_show_intval, &opt_avalon9_tbase,
+		     "Set Avalon9 tbase and use (0-8) bits, range 0-255."),
+	OPT_WITH_ARG("--avalon9-pid-p",
+		     set_int_0_to_9999, opt_show_intval, &opt_avalon9_pid_p,
+		     "Set Avalon9 pid-p, range 0-9999."),
+	OPT_WITH_ARG("--avalon9-pid-i",
+		     set_int_0_to_9999, opt_show_intval, &opt_avalon9_pid_i,
+		     "Set Avalon9 pid-i, range 0-9999."),
+	OPT_WITH_ARG("--avalon9-pid-d",
+		     set_int_0_to_9999, opt_show_intval, &opt_avalon9_pid_d,
+		     "Set Avalon9 pid-d, range 0-9999."),
+	OPT_WITH_CBARG("--avalon9-adjust-volt-info",
+		     set_avalon9_adjust_volt_info, NULL, &opt_set_avalon9_adjust_volt_info,
+		     "Set Avalon9 adjust volt info, range 0-9999"),
+#endif
+#ifdef USE_AVALONLC3
+	OPT_WITH_CBARG("--avalonlc3-voltage-level",
+		     set_avalonlc3_voltage_level, NULL, &opt_set_avalonlc3_voltage_level,
+		     "Set Avalonlc3 default level of core voltage, range:[0, 31], step: 1"),
+	OPT_WITH_CBARG("--avalonlc3-voltage-level-offset",
+		     set_avalonlc3_voltage_level_offset, NULL, &opt_set_avalonlc3_voltage_level_offset,
+		     "Set Avalonlc3 default offset of core voltage level, range:[-2, 1], step: 1"),
+	OPT_WITH_CBARG("--avalonlc3-freq",
+		     set_avalonlc3_freq, NULL, &opt_set_avalonlc3_freq,
+		     "Set Avalonlc3 default frequency, range:[25, 1200], step: 25, example: 800"),
+	OPT_WITH_ARG("--avalonlc3-freq-sel",
+		     set_int_0_to_4, opt_show_intval, &opt_avalonlc3_freq_sel,
+		     "Set Avalonlc3 default frequency select, range:[0, 4], step: 1, example: 3"),
+	OPT_WITH_CBARG("--avalonlc3-fan",
+		     set_avalonlc3_fan, NULL, &opt_set_avalonlc3_fan,
+		     "Set Avalonlc3 target fan speed, range:[0, 100], step: 1, example: 0-100"),
+	OPT_WITH_ARG("--avalonlc3-temp",
+		     set_int_0_to_100, opt_show_intval, &opt_avalonlc3_temp_target,
+		     "Set Avalonlc3 target temperature, range:[0, 100]"),
+	OPT_WITH_ARG("--avalonlc3-polling-delay",
+		     set_int_1_to_65535, opt_show_intval, &opt_avalonlc3_polling_delay,
+		     "Set Avalonlc3 polling delay value (ms)"),
+	OPT_WITH_ARG("--avalonlc3-aucspeed",
+		     opt_set_intval, opt_show_intval, &opt_avalonlc3_aucspeed,
+		     "Set AUC3 IIC bus speed"),
+	OPT_WITH_ARG("--avalonlc3-aucxdelay",
+		     opt_set_intval, opt_show_intval, &opt_avalonlc3_aucxdelay,
+		     "Set AUC3 IIC xfer read delay, 4800 ~= 1ms"),
+	OPT_WITH_ARG("--avalonlc3-smart-speed",
+		     opt_set_intval, opt_show_intval, &opt_avalonlc3_smart_speed,
+		     "Set Avalonlc3 smart speed, range 0-1. 0 means Disable"),
+	OPT_WITH_ARG("--avalonlc3-th-pass",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalonlc3_th_pass,
+		     "Set A3210M th pass value"),
+	OPT_WITH_ARG("--avalonlc3-th-fail",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalonlc3_th_fail,
+		     "Set A3210M th fail value"),
+	OPT_WITH_ARG("--avalonlc3-th-init",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalonlc3_th_init,
+		     "Set A3210M th init value"),
+	OPT_WITH_ARG("--avalonlc3-th-ms",
+		     set_int_0_to_65535, opt_show_intval, &opt_avalonlc3_th_ms,
+		     "Set A3210M th ms value"),
+	OPT_WITH_ARG("--avalonlc3-th-timeout",
+		     opt_set_uintval, opt_show_uintval, &opt_avalonlc3_th_timeout,
+		     "Set A3210M th timeout value"),
+	OPT_WITH_ARG("--avalonlc3-th-add",
+		     set_int_0_to_1, opt_show_intval, &opt_avalonlc3_th_add,
+		     "Set A3210M th add value"),
+	OPT_WITHOUT_ARG("--avalonlc3-iic-detect",
+		     opt_set_bool, &opt_avalonlc3_iic_detect,
+		     "Enable Avalonlc3 detect through iic controller"),
+	OPT_WITH_ARG("--avalonlc3-nonce-mask",
+		     set_int_24_to_32, opt_show_intval, &opt_avalonlc3_nonce_mask,
+		     "Set A3210M nonce mask, range 24-32."),
+	OPT_WITH_ARG("--avalonlc3-nonce-check",
+		     set_int_0_to_1, opt_show_intval, &opt_avalonlc3_nonce_check,
+		     "Set A3210M nonce check, range 0-1."),
+	OPT_WITH_ARG("--avalonlc3-roll-enable",
+		     set_int_0_to_1, opt_show_intval, &opt_avalonlc3_roll_enable,
+		     "Set A3210M roll enable, range 0-1."),
+	OPT_WITH_ARG("--avalonlc3-mux-l2h",
+		     set_int_0_to_2, opt_show_intval, &opt_avalonlc3_mux_l2h,
+		     "Set Avalonlc3 mux l2h, range 0-2."),
+	OPT_WITH_ARG("--avalonlc3-mux-h2l",
+		     set_int_0_to_1, opt_show_intval, &opt_avalonlc3_mux_h2l,
+		     "Set Avalonlc3 mux h2l, range 0-1."),
+	OPT_WITH_ARG("--avalonlc3-h2ltime0-spd",
+		     set_int_0_to_255, opt_show_intval, &opt_avalonlc3_h2ltime0_spd,
+		     "Set Avalonlc3 h2ltime0 spd, range 0-255."),
+	OPT_WITH_ARG("--avalonlc3-spdlow",
+		     set_int_0_to_3, opt_show_intval, &opt_avalonlc3_spdlow,
+		     "Set Avalonlc3 spdlow, range 0-3."),
+	OPT_WITH_ARG("--avalonlc3-spdhigh",
+		     set_int_0_to_3, opt_show_intval, &opt_avalonlc3_spdhigh,
+		     "Set Avalonlc3 spdhigh, range 0-3."),
+	OPT_WITH_ARG("--avalonlc3-tbase",
+		     set_int_0_to_255, opt_show_intval, &opt_avalonlc3_tbase,
+		     "Set Avalonlc3 tbase and use (0-8) bits, range 0-255."),
+	OPT_WITH_CBARG("--avalonlc3-cinfo-asic",
+		     set_avalonlc3_asic_otp, NULL, &opt_set_avalonlc3_asic_otp,
+		     "Set Avalonlc3 cinfo asic index, range:[0, 25], step: 1"),
+	OPT_WITH_ARG("--avalonlc3-pid-p",
+		     set_int_0_to_9999, opt_show_intval, &opt_avalonlc3_pid_p,
+		     "Set Avalonlc3 pid-p, range 0-9999."),
+	OPT_WITH_ARG("--avalonlc3-pid-i",
+		     set_int_0_to_9999, opt_show_intval, &opt_avalonlc3_pid_i,
+		     "Set Avalonlc3 pid-i, range 0-9999."),
+	OPT_WITH_ARG("--avalonlc3-pid-d",
+		     set_int_0_to_9999, opt_show_intval, &opt_avalonlc3_pid_d,
+		     "Set Avalonlc3 pid-d, range 0-9999."),
+#endif
+#ifdef USE_AVALON_MINER
+	OPT_WITH_CBARG("--avalonm-voltage",
+		     set_avalonm_voltage, NULL, &opt_set_avalonm_voltage,
+		     "Set Avalon miner core voltage, in millivolts, step: 125"),
+	OPT_WITH_CBARG("--avalonm-freq",
+		     set_avalonm_freq, NULL, &opt_set_avalonm_freq,
+		     "Set frequency for Avalon miner, 1 to 3 values, example: 275:250:200"),
+	OPT_WITH_ARG("--avalonm-ntime-offset",
+		     opt_set_intval, opt_show_intval, &opt_avalonm_ntime_offset,
+		     "Set Avalon miner ntime rolling max offset, range 0-4"),
+	OPT_WITH_ARG("--avalonm-spispeed",
+		     opt_set_intval, opt_show_intval, &opt_avalonm_spispeed,
+		     "Set spi speed for Avalon miner"),
+	OPT_WITHOUT_ARG("--avalonm-automatic-freq",
+			opt_set_bool, &opt_avalonm_autof,
+			"Automatic adjust frequency base on chip HW"),
+#endif
+#ifdef USE_BAB
+	OPT_WITH_ARG("--bab-options",
+		     opt_set_charp, NULL, &opt_bab_options,
+		     "Set bab options max:def:min:up:down:hz:delay:trf"),
+#endif
+	OPT_WITHOUT_ARG("--balance",
+		     set_balance, &pool_strategy,
+		     "Change multipool strategy from failover to even share balance"),
+	OPT_WITH_ARG("--benchfile",
+			opt_set_charp, NULL, &opt_benchfile,
+			"Run cgminer in benchmark mode using a work file - produces no shares"),
+	OPT_WITHOUT_ARG("--benchfile-display",
+			opt_set_bool, &opt_benchfile_display,
+			"Display each benchfile nonce found"),
+	OPT_WITHOUT_ARG("--benchmark",
+			opt_set_bool, &opt_benchmark,
+			"Run cgminer in benchmark mode - produces no shares"),
+#if defined(USE_BITFORCE)
+	OPT_WITHOUT_ARG("--bfl-range",
+			opt_set_bool, &opt_bfl_noncerange,
+			"Use nonce range on bitforce devices if supported"),
+#endif
+#ifdef USE_BFLSC
+	OPT_WITH_ARG("--bflsc-overheat",
+		     set_int_0_to_200, opt_show_intval, &opt_bflsc_overheat,
+		     "Set overheat temperature where BFLSC devices throttle, 0 to disable"),
+#endif
+#ifdef USE_AVALON
+	OPT_WITH_ARG("--bitburner-voltage",
+		     opt_set_intval, NULL, &opt_bitburner_core_voltage,
+		     "Set BitBurner (Avalon) core voltage, in millivolts"),
+	OPT_WITH_ARG("--bitburner-fury-voltage",
+		     opt_set_intval, NULL, &opt_bitburner_fury_core_voltage,
+		     "Set BitBurner Fury core voltage, in millivolts"),
+	OPT_WITH_ARG("--bitburner-fury-options",
+		     opt_set_charp, NULL, &opt_bitburner_fury_options,
+		     "Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq"),
+#endif
+#if defined(USE_ANT_S1) || defined(USE_ANT_S2)
+	OPT_WITH_ARG("--bitmain-cutoff",
+		     set_int_0_to_100, opt_show_intval, &opt_bitmain_overheat,
+		     "Set bitmain overheat cut off temperature"),
+	OPT_WITH_CBARG("--bitmain-fan",
+		     set_bitmain_fan, NULL, &opt_set_bitmain_fan,
+		     "Set fanspeed percentage for bitmain, single value or range (default: 20-100)"),
+	OPT_WITH_CBARG("--bitmain-freq",
+		     opt_set_charp, NULL, &opt_bitmain_freq,
+		     "Set bitmain freq options timeout:freq:regdata"),
+	OPT_WITHOUT_ARG("--bitmain-hwerror",
+			opt_set_bool, &opt_bitmain_hwerror,
+			"Set bitmain device detect hardware error"),
+	OPT_WITH_ARG("--bitmain-options",
+		     opt_set_charp, NULL, &opt_bitmain_options,
+#ifdef USE_ANT_S1
+		     "Set bitmain options baud:miners:asic:timeout:freq:regdata"
+#else
+		     "Set bitmain options baud:miners:asic:ignored..."
+#endif
+			),
+	OPT_WITH_ARG("--bitmain-temp",
+		     set_int_0_to_100, opt_show_intval, &opt_bitmain_temp,
+		     "Set bitmain target temperature"),
+	OPT_WITH_ARG("--bitmain-workdelay",
+		     set_int_0_to_100, opt_show_intval, &opt_bitmain_workdelay,
+		     "Set bitmain work delay (ms) 0-100"),
+	// Ignored
+	OPT_WITHOUT_ARG("--bitmain-auto",
+			opt_set_bool, &opt_bitmain_auto,
+			opt_hidden),
+	OPT_WITHOUT_ARG("--bitmain-nobeeper",
+			opt_set_bool, &opt_bitmain_nobeeper,
+			opt_hidden),
+	OPT_WITHOUT_ARG("--bitmain-notempoverctrl",
+			opt_set_bool, &opt_bitmain_notempoverctrl,
+			opt_hidden),
+#ifdef USE_ANT_S1
+	// S1 has no effect
+	OPT_WITHOUT_ARG("--bitmainbeeper",
+			opt_set_bool, &opt_bitmain_beeper,
+			opt_hidden),
+	OPT_WITHOUT_ARG("--bitmaintempoverctrl",
+			opt_set_bool, &opt_bitmain_tempoverctrl,
+			opt_hidden),
+	OPT_WITHOUT_ARG("--bitmain-homemode",
+			opt_set_bool, &opt_bitmain_homemode,
+			opt_hidden),
+#endif
+#endif
+#ifdef USE_ANT_S2
+	OPT_WITH_ARG("--bitmain-voltage",
+		     opt_set_charp, NULL, &opt_bitmain_voltage,
+		     "Set bitmain voltage (default: "BITMAIN_VOLTAGE_DEF")"),
+#ifndef USE_ANT_S3
+	OPT_WITH_ARG("--bitmain-dev",
+		     opt_set_charp, NULL, &opt_bitmain_dev,
+		     "Set bitmain device"),
+#endif
+	OPT_WITHOUT_ARG("--bitmainbeeper",
+			opt_set_bool, &opt_bitmain_beeper,
+			"Set bitmain beeper ringing"),
+	OPT_WITHOUT_ARG("--bitmain-checkall",
+			opt_set_bool, &opt_bitmain_checkall,
+			opt_hidden),
+	OPT_WITHOUT_ARG("--bitmain-checkn2diff",
+			opt_set_bool, &opt_bitmain_checkn2diff,
+			opt_hidden),
+	OPT_WITHOUT_ARG("--bitmaintempoverctrl",
+			opt_set_bool, &opt_bitmain_tempoverctrl,
+			"Set bitmain stop runing when temprerature is over 80 degree Celsius"),
+	OPT_WITHOUT_ARG("--bitmain-homemode",
+			opt_set_bool, &opt_bitmain_homemode,
+			"Set bitmain miner to home mode"),
+#endif
+#ifdef USE_BITMINE_A1
+	OPT_WITH_ARG("--bitmine-a1-options",
+		     opt_set_charp, NULL, &opt_bitmine_a1_options,
+		     "Bitmine A1 options ref_clk_khz:sys_clk_khz:spi_clk_khz:override_chip_num"),
+#endif
+	OPT_WITHOUT_ARG("--block-check",
+			opt_set_bool, &opt_blockcheck,
+			"Run a block diff check of the binary then exit"),
+#ifdef USE_BITFURY
+	OPT_WITH_ARG("--bxf-bits",
+		     set_int_32_to_63, opt_show_intval, &opt_bxf_bits,
+		     "Set max BXF/HXF bits for overclocking"),
+	OPT_WITH_ARG("--bxf-debug",
+		     set_int_0_to_4, opt_show_intval, &opt_bxf_debug,
+		    "BXF: Debug all USB I/O, > is to the board(s), < is from the board(s)"),
+	OPT_WITH_ARG("--bxf-temp-target",
+		     set_int_0_to_200, opt_show_intval, &opt_bxf_temp_target,
+		     "Set target temperature for BXF/HXF devices"),
+	OPT_WITH_ARG("--bxm-bits",
+		     set_int_0_to_100, opt_show_intval, &opt_bxm_bits,
+		     "Set BXM bits for overclocking"),
+#endif
+#ifdef USE_BITFURY16
+	OPT_WITHOUT_ARG("--bf16-set-clock",
+			opt_set_bool, &opt_bf16_set_clock,
+			"Set clock to all chips"),
+	OPT_WITH_ARG("--bf16-test-chip",
+			opt_set_charp, NULL, &opt_bf16_test_chip,
+			"Test BF16 chip communication: [board_id:bcm250_id:chip_id]"),
+	OPT_WITH_ARG("--bf16-clock",
+			opt_set_charp, NULL, &opt_bf16_clock,
+			"BF16 chips clock value"),
+	OPT_WITH_ARG("--bf16-renonce-clock",
+			opt_set_charp, NULL, &opt_bf16_renonce_clock,
+			"BF16 renonce chip clock value"),
+	OPT_WITHOUT_ARG("--bf16-enable-stats",
+			opt_set_bool, &opt_bf16_stats_enabled,
+			"Enable statistics thread"),
+	OPT_WITHOUT_ARG("--bf16-disable-power-management",
+			opt_set_bool, &opt_bf16_power_management_disabled,
+			"Disable automatic power management"),
+	OPT_WITH_ARG("--bf16-renonce",
+			set_int_0_to_2, NULL, &opt_bf16_renonce,
+			"Renonce functionality: 0 - disabled, 1 - one chip, 2 - chip per board"),
+#ifdef MINER_X5
+	OPT_WITHOUT_ARG("--bf16-manual-pid-enable",
+			opt_set_bool, &opt_bf16_manual_pid_enabled,
+			"Enable manual PID regulator"),
+#endif
+#ifdef MINER_X6
+	OPT_WITHOUT_ARG("--bf16-manual-pid-disable",
+			opt_set_bool, &opt_bf16_manual_pid_disabled,
+			"Disable manual PID regulator"),
+#endif
+	OPT_WITH_ARG("--bf16-fan-speed",
+		     set_int_0_to_100, NULL, &opt_bf16_fan_speed,
+		     "Set fan speed in '%' range (0 - 100)"),
+	OPT_WITH_ARG("--bf16-target-temp",
+		     set_int_0_to_100, NULL, &opt_bf16_target_temp,
+		     "Set control board target temperature range (0 - 100)"),
+	OPT_WITH_ARG("--bf16-alarm-temp",
+		     set_int_0_to_100, NULL, &opt_bf16_alarm_temp,
+		     "Set control board alarm temperature range (0 - 100)"),
+#endif
+#ifdef USE_BITMAIN_SOC
+	OPT_WITH_ARG("--version-file",
+	set_version_path, NULL, opt_hidden,
+	"Set miner version file"),
+	
+	OPT_WITHOUT_ARG("--bitmain-fan-ctrl",
+	opt_set_bool, &opt_bitmain_fan_ctrl,
+	"Enable bitmain miner fan controlling"),
+
+	OPT_WITH_ARG("--bitmain-fan-pwm",
+	set_int_0_to_100, opt_show_intval, &opt_bitmain_fan_pwm,
+	"Set bitmain fan pwm percentage 0~100"),
+
+	OPT_WITH_ARG("--bitmain-freq",
+	set_int_0_to_9999,opt_show_intval, &opt_bitmain_soc_freq,
+	"Set frequency"),
+
+	OPT_WITH_ARG("--bitmain-voltage",
+	set_int_0_to_9999,opt_show_intval, &opt_bitmain_soc_voltage,
+	"Set voltage"),
+
+	OPT_WITHOUT_ARG("--fixed-freq",
+	opt_set_bool, &opt_fixed_freq,
+	"Set bitmain miner use fixed freq"),
+
+	OPT_WITHOUT_ARG("--no-pre-heat",
+	opt_set_false, &opt_pre_heat,
+	"Set bitmain miner doesn't pre heat"),
+
+	OPT_WITH_ARG("--multi-version",
+	opt_set_intval, NULL, &opt_multi_version,
+	"Multi version mining!"),
+#endif
+#ifdef USE_BLOCKERUPTER
+        OPT_WITH_ARG("--bet-clk",
+                     opt_set_intval, opt_show_intval, &opt_bet_clk,
+                     "Set Block Erupter clock"),
+#endif
+#ifdef USE_GEKKO
+	OPT_WITH_ARG("--gekko-serial",
+			 opt_set_charp, NULL, &opt_gekko_serial,
+			 "Detect GekkoScience Device by Serial Number"),
+	OPT_WITHOUT_ARG("--gekko-compac-detect",
+			 opt_set_bool, &opt_gekko_gsc_detect,
+			 "Detect GekkoScience Compac BM1384"),
+	OPT_WITHOUT_ARG("--gekko-2pac-detect",
+			 opt_set_bool, &opt_gekko_gsd_detect,
+			 "Detect GekkoScience 2Pac BM1384"),
+	OPT_WITHOUT_ARG("--gekko-terminus-detect",
+			 opt_set_bool, &opt_gekko_gse_detect,
+			 "Detect GekkoScience Terminus BM1384"),
+	OPT_WITHOUT_ARG("--gekko-newpac-detect",
+			 opt_set_bool, &opt_gekko_gsh_detect,
+			 "Detect GekkoScience NewPac BM1387"),
+	OPT_WITHOUT_ARG("--gekko-r606-detect",
+			 opt_set_bool, &opt_gekko_gsi_detect,
+			 "Detect GekkoScience Terminus BM1387"),
+	OPT_WITHOUT_ARG("--gekko-compacf-detect",
+			 opt_set_bool, &opt_gekko_gsf_detect,
+			 "Detect GekkoScience CompacF BM1397"),
+	OPT_WITHOUT_ARG("--gekko-r909-detect",
+			 opt_set_bool, &opt_gekko_r909_detect,
+			 "Detect GekkoScience Terminus R909 BM1397"),
+	OPT_WITHOUT_ARG("--gekko-noboost",
+			 opt_set_bool, &opt_gekko_noboost,
+			 "Disable GekkoScience NewPac/R606/CompacF AsicBoost"),
+	OPT_WITHOUT_ARG("--gekko-lowboost",
+			 opt_set_bool, &opt_gekko_lowboost,
+			 "GekkoScience NewPac/R606 AsicBoost - 2 midstate"),
+	OPT_WITH_ARG("--gekko-terminus-freq",
+		     set_float_0_to_500, opt_show_floatval, &opt_gekko_gse_freq,
+		     "Set GekkoScience Terminus BM1384 frequency in MHz, range 6.25-500"),
+	OPT_WITH_ARG("--gekko-2pac-freq",
+		     set_float_0_to_500, opt_show_floatval, &opt_gekko_gsd_freq,
+		     "Set GekkoScience 2Pac BM1384 frequency in MHz, range 6.25-500"),
+	OPT_WITH_ARG("--gekko-compac-freq",
+		     set_float_0_to_500, opt_show_floatval, &opt_gekko_gsc_freq,
+		     "Set GekkoScience Compac BM1384 frequency in MHz, range 6.25-500"),
+	OPT_WITH_ARG("--gekko-tune-down",
+		     set_float_0_to_500, opt_show_floatval, &opt_gekko_tune_down,
+		     "Set GekkoScience miner minimum hash quality, range 0-100"),
+	OPT_WITH_ARG("--gekko-tune-up",
+		     set_float_0_to_500, opt_show_floatval, &opt_gekko_tune_up,
+		     "Set GekkoScience miner ramping hash threshold, rante 0-99"),
+	OPT_WITH_ARG("--gekko-wait-factor",
+		     set_float_0_to_500, opt_show_floatval, &opt_gekko_wait_factor,
+		     "Set GekkoScience miner task send wait factor, range 0.01-2.00"),
+	OPT_WITH_ARG("--gekko-bauddiv",
+		     set_int_0_to_9999, opt_show_intval, &opt_gekko_bauddiv,
+		     "Set GekkoScience BM1387 baud divider {0: auto, 1: 1.5M, 7: 375K, 13: 214K, 25: 115K}"),
+	OPT_WITH_ARG("--gekko-newpac-freq",
+		     set_int_0_to_9999, opt_show_intval, &opt_gekko_gsh_freq,
+		     "Set GekkoScience NewPac BM1387 frequency in MHz, range 50-900"),
+	OPT_WITH_ARG("--gekko-r606-freq",
+		     set_int_0_to_9999, opt_show_intval, &opt_gekko_gsi_freq,
+		     "Set GekkoScience Terminus R606 frequency in MHz, range 50-900"),
+	OPT_WITH_ARG("--gekko-compacf-freq",
+		     set_int_0_to_9999, opt_show_intval, &opt_gekko_gsf_freq,
+		     "Set GekkoScience CompacF BM1397 frequency in MHz, range 100-800"),
+	OPT_WITH_ARG("--gekko-r909-freq",
+		     set_int_0_to_9999, opt_show_intval, &opt_gekko_r909_freq,
+		     "Set GekkoScience Terminus R909 BM1397 frequency in MHz, range 100-800"),
+	OPT_WITH_ARG("--gekko-start-freq",
+		     set_int_0_to_9999, opt_show_intval, &opt_gekko_start_freq,
+                     "Ramp start frequency MHz 25-500"),
+	OPT_WITH_ARG("--gekko-step-freq",
+		     set_float_0_to_500, opt_show_intval, &opt_gekko_step_freq,
+		     "Ramp frequency step MHz 1-100"),
+	OPT_WITH_ARG("--gekko-step-delay",
+		     set_int_0_to_9999, opt_show_intval, &opt_gekko_step_delay,
+		     "Ramp step interval range 1-600"),
+	OPT_WITHOUT_ARG("--gekko-mine2",
+			opt_set_bool, &opt_gekko_mine2, opt_hidden), // ignored
+	OPT_WITH_ARG("--gekko-tune2",
+			set_int_0_to_9999, opt_show_intval, &opt_gekko_tune2,
+			"Tune up mine2 mins 30-9999, default 0=never"),
+#endif
+#ifdef HAVE_LIBCURL
+	OPT_WITH_ARG("--btc-address",
+		     opt_set_charp, NULL, &opt_btc_address,
+		     "Set bitcoin target address when solo mining to bitcoind (mandatory)"),
+	OPT_WITH_ARG("--btc-sig",
+		     opt_set_charp, NULL, &opt_btc_sig,
+		     "Set signature to add to coinbase when solo mining (optional)"),
+#endif
+#ifdef HAVE_CURSES
+	OPT_WITHOUT_ARG("--compact",
+			opt_set_bool, &opt_compact,
+			"Use compact display without per device statistics"),
+#endif
+#ifdef USE_COINTERRA
+	OPT_WITH_ARG("--cta-load",
+		set_int_0_to_255, opt_show_intval, &opt_cta_load,
+		"Set load for CTA devices, 0-255 range"),
+	OPT_WITH_ARG("--ps-load",
+		set_int_0_to_100, opt_show_intval, &opt_ps_load,
+		"Set power supply load for CTA devices, 0-100 range"),
+#endif
+	OPT_WITHOUT_ARG("--debug|-D",
+		     enable_debug, &opt_debug,
+		     "Enable debug output"),
+#ifdef HAVE_CURSES
+	OPT_WITHOUT_ARG("--decode",
+			opt_set_bool, &opt_decode,
+			"Decode 2nd pool stratum coinbase transactions (1st must be bitcoind) and exit"),
+#endif
+	OPT_WITHOUT_ARG("--disable-rejecting",
+			opt_set_bool, &opt_disable_pool,
+			"Automatically disable pools that continually reject shares"),
+#ifdef USE_DRAGONMINT_T1
+	OPT_WITH_ARG("--dragonmint-t1-options",
+		     opt_set_charp, NULL, &opt_dragonmint_t1_options,
+	             "Dragonmint T1 options ref_clk_khz:sys_clk_khz:spi_clk_khz:override_chip_num"),
+	OPT_WITHOUT_ARG("--T1efficient",
+			opt_set_bool, &opt_T1_efficient,
+		        "Tune Dragonmint T1 per chain voltage and frequency for optimal efficiency"),
+	OPT_WITHOUT_ARG("--T1factory",
+			opt_set_invbool, &opt_T1auto,
+		        opt_hidden),
+	OPT_WITHOUT_ARG("--T1noauto",
+			opt_set_invbool, &opt_T1auto,
+			"Disable Dragonmint T1 per chain auto voltage and frequency tuning"),
+	OPT_WITHOUT_ARG("--T1performance",
+			opt_set_bool, &opt_T1_performance,
+		        "Tune Dragonmint T1 per chain voltage and frequency for maximum performance"),
+	OPT_WITH_ARG("--T1fantarget",
+			opt_set_intval, opt_show_intval, &opt_T1_target,
+			"Throttle T1 frequency to keep fan less than target fan speed"),
+	OPT_WITH_ARG("--T1Pll1",
+		     set_int_0_to_9999, opt_show_intval, &opt_T1Pll[0],
+	            "Set PLL Clock 1 in Dragonmint T1 broad 1 chip (-1: 1000MHz, >0:Lookup PLL table)"),
+	OPT_WITH_ARG("--T1Pll2",
+		     set_int_0_to_9999, opt_show_intval, &opt_T1Pll[1],
+	            "Set PLL Clock 2 in Dragonmint T1 broad 1 chip (-1: 1000MHz, >0:Lookup PLL table)"),
+	OPT_WITH_ARG("--T1Pll3",
+		     set_int_0_to_9999, opt_show_intval, &opt_T1Pll[2],
+	            "Set PLL Clock 3 in Dragonmint T1 broad 1 chip (-1: 1000MHz, >0:Lookup PLL table)"),
+	OPT_WITH_ARG("--T1Pll4",
+		     set_int_0_to_9999, opt_show_intval, &opt_T1Pll[3],
+	            "Set PLL Clock 4 in Dragonmint T1 broad 1 chip (-1: 1000MHz, >0:Lookup PLL table)"),
+	OPT_WITH_ARG("--T1Pll5",
+		     set_int_0_to_9999, opt_show_intval, &opt_T1Pll[4],
+	            "Set PLL Clock 5 in Dragonmint T1 broad 1 chip (-1: 1000MHz, >0:Lookup PLL table)"),
+	OPT_WITH_ARG("--T1Pll6",
+		     set_int_0_to_9999, opt_show_intval, &opt_T1Pll[5],
+	            "Set PLL Clock 6 in Dragonmint T1 broad 1 chip (-1: 1000MHz, >0:Lookup PLL table)"),
+	OPT_WITH_ARG("--T1Pll7",
+		     set_int_0_to_9999, opt_show_intval, &opt_T1Pll[6],
+	            "Set PLL Clock 7 in Dragonmint T1 broad 1 chip (-1: 1000MHz, >0:Lookup PLL table)"),
+	OPT_WITH_ARG("--T1Pll8",
+		     set_int_0_to_9999, opt_show_intval, &opt_T1Pll[7],
+	            "Set PLL Clock 8 in Dragonmint T1 broad 1 chip (-1: 1000MHz, >0:Lookup PLL table)"),
+	OPT_WITH_ARG("--T1Volt1",
+	     set_int_voltage, opt_show_intval, &opt_T1Vol[0],
+	     "Dragonmint T1 set voltage 1 - VID overrides if set (390-425)"),
+	OPT_WITH_ARG("--T1Volt2",
+	     set_int_voltage, opt_show_intval, &opt_T1Vol[1],
+	     "Dragonmint T1 set voltage 2 - VID overrides if set (390-425)"),
+	OPT_WITH_ARG("--T1Volt3",
+	     set_int_voltage, opt_show_intval, &opt_T1Vol[2],
+	     "Dragonmint T1 set voltage 3 - VID overrides if set (390-425)"),
+	OPT_WITH_ARG("--T1Volt4",
+	     set_int_voltage, opt_show_intval, &opt_T1Vol[3],
+	     "Dragonmint T1 set voltage 4 - VID overrides if set (390-425)"),
+	OPT_WITH_ARG("--T1Volt5",
+	     set_int_voltage, opt_show_intval, &opt_T1Vol[4],
+	     "Dragonmint T1 set voltage 5 - VID overrides if set (390-425)"),
+	OPT_WITH_ARG("--T1Volt6",
+	     set_int_voltage, opt_show_intval, &opt_T1Vol[5],
+	     "Dragonmint T1 set voltage 6 - VID overrides if set (390-425)"),
+	OPT_WITH_ARG("--T1Volt7",
+	     set_int_voltage, opt_show_intval, &opt_T1Vol[6],
+	     "Dragonmint T1 set voltage 7 - VID overrides if set (390-425)"),
+	OPT_WITH_ARG("--T1Volt8",
+	     set_int_voltage, opt_show_intval, &opt_T1Vol[7],
+	     "Dragonmint T1 set voltage 8 - VID overrides if set (390-425)"),
+	OPT_WITH_ARG("--T1VID1",
+	     set_int_1_to_31, opt_show_intval, &opt_T1VID[0],
+	     "Dragonmint T1 set VID 1 in noauto - Overrides voltage if set (1-31)"),
+	OPT_WITH_ARG("--T1VID2",
+	     set_int_1_to_31, opt_show_intval, &opt_T1VID[1],
+	     "Dragonmint T1 set VID 2 in noauto - Overrides voltage if set (1-31)"),
+	OPT_WITH_ARG("--T1VID3",
+	     set_int_1_to_31, opt_show_intval, &opt_T1VID[2],
+	     "Dragonmint T1 set VID 3 in noauto - Overrides voltage if set (1-31)"),
+	OPT_WITH_ARG("--T1VID4",
+	     set_int_1_to_31, opt_show_intval, &opt_T1VID[3],
+	     "Dragonmint T1 set VID 4 in noauto - Overrides voltage if set (1-31)"),
+	OPT_WITH_ARG("--T1VID5",
+	     set_int_1_to_31, opt_show_intval, &opt_T1VID[4],
+	     "Dragonmint T1 set VID 5 in noauto - Overrides voltage if set (1-31)"),
+	OPT_WITH_ARG("--T1VID6",
+	     set_int_1_to_31, opt_show_intval, &opt_T1VID[5],
+	     "Dragonmint T1 set VID 6 in noauto - Overrides voltage if set (1-31)"),
+	OPT_WITH_ARG("--T1VID7",
+	     set_int_1_to_31, opt_show_intval, &opt_T1VID[6],
+	     "Dragonmint T1 set VID 7 in noauto - Overrides voltage if set (1-31)"),
+	OPT_WITH_ARG("--T1VID8",
+	     set_int_1_to_31, opt_show_intval, &opt_T1VID[7],
+	     "Dragonmint T1 set VID 8 in noauto - Overrides voltage if set (1-31)"),
+#endif
+#ifdef USE_DRILLBIT
+        OPT_WITH_ARG("--drillbit-options",
+		     opt_set_charp, NULL, &opt_drillbit_options,
+		     "Set drillbit options <int|ext>:clock[:clock_divider][:voltage]"),
+        OPT_WITH_ARG("--drillbit-auto",
+		     opt_set_charp, NULL, &opt_drillbit_auto,
+		     "Enable drillbit automatic tuning <every>:[<gooderr>:<baderr>:<maxerr>]"),
+#endif
+	OPT_WITH_ARG("--expiry|-E",
+		     set_null, NULL, &opt_set_null,
+		     opt_hidden),
+#ifdef USE_XTRANONCE
+	OPT_WITHOUT_ARG("--extranonce-subscribe",
+			set_extranonce_subscribe, NULL,
+			"Enable 'extranonce' stratum subscribe"),
+#endif
+	OPT_WITHOUT_ARG("--failover-only",
+			set_null, &opt_set_null,
+			opt_hidden),
+	OPT_WITH_ARG("--fallback-time",
+		     opt_set_intval, opt_show_intval, &opt_pool_fallback,
+		     "Set time in seconds to fall back to a higher priority pool after period of instability"),
+	OPT_WITHOUT_ARG("--fix-protocol",
+			opt_set_bool, &opt_fix_protocol,
+			"Do not redirect to stratum protocol from GBT"),
+#ifdef USE_HASHFAST
+	OPT_WITHOUT_ARG("--hfa-dfu-boot",
+			opt_set_bool, &opt_hfa_dfu_boot,
+			opt_hidden),
+	OPT_WITH_ARG("--hfa-hash-clock",
+		     set_int_0_to_9999, opt_show_intval, &opt_hfa_hash_clock,
+		     "Set hashfast clock speed"),
+	OPT_WITH_ARG("--hfa-fail-drop",
+		     set_int_0_to_100, opt_show_intval, &opt_hfa_fail_drop,
+		     "Set how many MHz to drop clockspeed each failure on an overlocked hashfast device"),
+	OPT_WITH_CBARG("--hfa-fan",
+		     set_hfa_fan, NULL, &opt_set_hfa_fan,
+		     "Set fanspeed percentage for hashfast, single value or range (default: 10-85)"),
+	OPT_WITH_ARG("--hfa-name",
+		     opt_set_charp, NULL, &opt_hfa_name,
+		     "Set a unique name for a single hashfast device specified with --usb or the first device found"),
+	OPT_WITHOUT_ARG("--hfa-noshed",
+			opt_set_bool, &opt_hfa_noshed,
+			"Disable hashfast dynamic core disabling feature"),
+	OPT_WITH_ARG("--hfa-ntime-roll",
+		     opt_set_intval, NULL, &opt_hfa_ntime_roll,
+		     opt_hidden),
+	OPT_WITH_ARG("--hfa-options",
+		     opt_set_charp, NULL, &opt_hfa_options,
+		     "Set hashfast options name:clock (comma separated)"),
+	OPT_WITHOUT_ARG("--hfa-pll-bypass",
+			opt_set_bool, &opt_hfa_pll_bypass,
+			opt_hidden),
+	OPT_WITH_ARG("--hfa-temp-overheat",
+		     set_int_0_to_200, opt_show_intval, &opt_hfa_overheat,
+		     "Set the hashfast overheat throttling temperature"),
+	OPT_WITH_ARG("--hfa-temp-target",
+		     set_int_0_to_200, opt_show_intval, &opt_hfa_target,
+		     "Set the hashfast target temperature (0 to disable)"),
+#endif
+#ifdef USE_HASHRATIO
+	OPT_WITH_CBARG("--hro-freq",
+		       set_hashratio_freq, NULL, &opt_hashratio_freq,
+		       "Set the hashratio clock frequency"),
+#endif
+	OPT_WITH_ARG("--hotplug",
+		     set_int_0_to_9999, NULL, &hotplug_time,
+#ifdef USE_USBUTILS
+		     "Seconds between hotplug checks (0 means never check)"
+#else
+		     opt_hidden
+#endif
+		    ),
+#ifdef USE_ICARUS
+	OPT_WITH_ARG("--icarus-options",
+		     opt_set_charp, NULL, &opt_icarus_options,
+		     opt_hidden),
+	OPT_WITH_ARG("--icarus-timing",
+		     opt_set_charp, NULL, &opt_icarus_timing,
+		     opt_hidden),
+#endif
+#if defined(HAVE_MODMINER)
+	OPT_WITH_ARG("--kernel-path|-K",
+		     opt_set_charp, opt_show_charp, &opt_kernel_path,
+	             "Specify a path to where bitstream files are"),
+#endif
+#ifdef USE_KLONDIKE
+	OPT_WITH_ARG("--klondike-options",
+		     opt_set_charp, NULL, &opt_klondike_options,
+		     "Set klondike options clock:temptarget"),
+#endif
+	OPT_WITHOUT_ARG("--load-balance",
+		     set_loadbalance, &pool_strategy,
+		     "Change multipool strategy from failover to quota based balance"),
+	OPT_WITH_ARG("--log|-l",
+		     set_int_0_to_9999, opt_show_intval, &opt_log_interval,
+		     "Interval in seconds between log output"),
+	OPT_WITHOUT_ARG("--lowmem",
+			opt_set_bool, &opt_lowmem,
+			"Minimise caching of shares for low memory applications"),
+	OPT_WITHOUT_ARG("--mac-yield",
+			opt_set_bool, &opt_mac_yield,
+			"Allow yield on old macs (default dont)"),
+#ifdef USE_MINION
+	OPT_WITH_ARG("--minion-chipreport",
+		     set_int_0_to_100, opt_show_intval, &opt_minion_chipreport,
+		     "Seconds to report chip 5min hashrate, range 0-100 (default: 0=disabled)"),
+	OPT_WITH_ARG("--minion-cores",
+		     opt_set_charp, NULL, &opt_minion_cores,
+		     opt_hidden),
+	OPT_WITHOUT_ARG("--minion-extra",
+		     opt_set_bool, &opt_minion_extra,
+		     opt_hidden),
+	OPT_WITH_ARG("--minion-freq",
+		     opt_set_charp, NULL, &opt_minion_freq,
+		     "Set minion chip frequencies in MHz, single value or comma list, range 100-1400 (default: 1200)"),
+	OPT_WITH_ARG("--minion-freqchange",
+		     set_int_0_to_9999, opt_show_intval, &opt_minion_freqchange,
+		     "Millisecond total time to do frequency changes (default: 1000)"),
+	OPT_WITH_ARG("--minion-freqpercent",
+		     set_int_0_to_100, opt_show_intval, &opt_minion_freqpercent,
+		     "Percentage to use when starting up a chip (default: 70%)"),
+	OPT_WITHOUT_ARG("--minion-idlecount",
+		     opt_set_bool, &opt_minion_idlecount,
+		     "Report when IdleCount is >0 or changes"),
+	OPT_WITH_ARG("--minion-ledcount",
+		     set_int_0_to_100, opt_show_intval, &opt_minion_ledcount,
+		     "Turn off led when more than this many chips below the ledlimit (default: 0)"),
+	OPT_WITH_ARG("--minion-ledlimit",
+		     set_int_0_to_200, opt_show_intval, &opt_minion_ledlimit,
+		     "Turn off led when chips GHs are below this (default: 90)"),
+	OPT_WITHOUT_ARG("--minion-noautofreq",
+		     opt_set_bool, &opt_minion_noautofreq,
+		     "Disable automatic frequency adjustment"),
+	OPT_WITHOUT_ARG("--minion-overheat",
+		     opt_set_bool, &opt_minion_overheat,
+		     "Enable directly halting any chip when the status exceeds 100C"),
+	OPT_WITH_ARG("--minion-spidelay",
+		     set_int_0_to_9999, opt_show_intval, &opt_minion_spidelay,
+		     "Add a delay in microseconds after each SPI I/O"),
+	OPT_WITH_ARG("--minion-spireset",
+		     opt_set_charp, NULL, &opt_minion_spireset,
+		     "SPI regular reset: iNNN for I/O count or sNNN for seconds - 0 means none"),
+	OPT_WITH_ARG("--minion-spisleep",
+		     set_int_0_to_9999, opt_show_intval, &opt_minion_spisleep,
+		     "Sleep time in milliseconds when doing an SPI reset"),
+	OPT_WITH_ARG("--minion-spiusec",
+		     set_int_0_to_9999, NULL, &opt_minion_spiusec,
+		     opt_hidden),
+	OPT_WITH_ARG("--minion-temp",
+		     opt_set_charp, NULL, &opt_minion_temp,
+		     "Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)"),
+#endif
+#if defined(unix) || defined(__APPLE__)
+	OPT_WITH_ARG("--monitor|-m",
+		     opt_set_charp, NULL, &opt_stderr_cmd,
+		     "Use custom pipe cmd for output messages"),
+#endif // defined(unix)
+#ifdef USE_BITFURY
+	OPT_WITH_ARG("--nfu-bits",
+		     set_int_32_to_63, opt_show_intval, &opt_nfu_bits,
+		     "Set nanofury bits for overclocking, range 32-63"),
+#endif
+	OPT_WITHOUT_ARG("--net-delay",
+			opt_set_bool, &opt_delaynet,
+			"Impose small delays in networking to not overload slow routers"),
+	OPT_WITHOUT_ARG("--no-pool-disable",
+			opt_set_invbool, &opt_disable_pool,
+			opt_hidden),
+	OPT_WITHOUT_ARG("--no-submit-stale",
+			opt_set_invbool, &opt_submit_stale,
+		        "Don't submit shares if they are detected as stale"),
+#ifdef USE_BITFURY
+	OPT_WITH_ARG("--osm-led-mode",
+		     set_int_0_to_4, opt_show_intval, &opt_osm_led_mode,
+		     "Set LED mode for OneStringMiner devices"),
+#endif
+	OPT_WITH_ARG("--pass|-p",
+		     set_pass, NULL, &opt_set_null,
+		     "Password for bitcoin JSON-RPC server"),
+	OPT_WITHOUT_ARG("--per-device-stats",
+			opt_set_bool, &want_per_device_stats,
+			"Force verbose mode and output per-device statistics"),
+	OPT_WITH_ARG("--pools",
+			opt_set_bool, NULL, &opt_set_null, opt_hidden),
+	OPT_WITHOUT_ARG("--protocol-dump|-P",
+			opt_set_bool, &opt_protocol,
+			"Verbose dump of protocol-level activities"),
+	OPT_WITH_ARG("--queue|-Q",
+		     set_null, NULL, &opt_set_null,
+		     opt_hidden),
+	OPT_WITHOUT_ARG("--quiet|-q",
+			opt_set_bool, &opt_quiet,
+			"Disable logging output, display status and errors"),
+	OPT_WITH_ARG("--quota|-U",
+		     set_quota, NULL, &opt_set_null,
+		     "quota;URL combination for server with load-balance strategy quotas"),
+	OPT_WITHOUT_ARG("--real-quiet",
+			opt_set_bool, &opt_realquiet,
+			"Disable all output"),
+	OPT_WITH_ARG("--retries",
+		     set_null, NULL, &opt_set_null,
+		     opt_hidden),
+	OPT_WITH_ARG("--retry-pause",
+		     set_null, NULL, &opt_set_null,
+		     opt_hidden),
+#ifdef USE_ICARUS
+	OPT_WITH_ARG("--rock-freq",
+		     set_float_125_to_500, &opt_show_floatval, &opt_rock_freq,
+		     "Set RockMiner frequency in MHz, range 125-500"),
+#endif
+	OPT_WITH_ARG("--rotate",
+		     set_rotate, NULL, &opt_set_null,
+		     "Change multipool strategy from failover to regularly rotate at N minutes"),
+	OPT_WITHOUT_ARG("--round-robin",
+		     set_rr, &pool_strategy,
+		     "Change multipool strategy from failover to round robin on failure"),
+#ifdef USE_FPGA_SERIAL
+	OPT_WITH_CBARG("--scan-serial|-S",
+		     add_serial, NULL, &opt_add_serial,
+		     "Serial port to probe for Serial FPGA Mining device"),
+#endif
+	OPT_WITH_ARG("--scan-time|-s",
+		     set_null, NULL, &opt_set_null,
+		     opt_hidden),
+	OPT_WITH_CBARG("--sched-start",
+		     set_sched_start, NULL, &opt_set_sched_start,
+		     "Set a time of day in HH:MM to start mining (a once off without a stop time)"),
+	OPT_WITH_CBARG("--sched-stop",
+		     set_sched_stop, NULL, &opt_set_sched_stop,
+		     "Set a time of day in HH:MM to stop mining (will quit without a start time)"),
+	OPT_WITH_CBARG("--sharelog",
+		     set_sharelog, NULL, &opt_set_sharelog,
+		     "Append share log to file"),
+	OPT_WITH_ARG("--shares",
+		     opt_set_intval, NULL, &opt_shares,
+		     "Quit after mining N shares (default: unlimited)"),
+	OPT_WITH_ARG("--socks-proxy",
+		     opt_set_charp, NULL, &opt_socks_proxy,
+		     "Set socks4 proxy (host:port)"),
+	OPT_WITH_ARG("--suggest-diff",
+		     opt_set_intval, NULL, &opt_suggest_diff,
+		     "Suggest miner difficulty for pool to user (default: none)"),
+#ifdef HAVE_SYSLOG_H
+	OPT_WITHOUT_ARG("--syslog",
+			opt_set_bool, &use_syslog,
+			"Use system log for output messages (default: standard error)"),
+#endif
+#if defined(USE_BITFORCE) || defined(USE_MODMINER) || defined(USE_BFLSC)
+	OPT_WITH_CBARG("--temp-cutoff",
+		     set_temp_cutoff, opt_show_intval, &opt_set_temp_cutoff,
+		     "Temperature where a device will be automatically disabled, one value or comma separated list"),
+#endif
+	OPT_WITHOUT_ARG("--text-only|-T",
+			opt_set_invbool, &use_curses,
+#ifdef HAVE_CURSES
+			"Disable ncurses formatted screen output"
+#else
+			opt_hidden
+#endif
+	),
+	OPT_WITH_ARG("--url|-o",
+		     set_url, NULL, &opt_set_null,
+		     "URL for bitcoin JSON-RPC server"),
+#ifdef USE_USBUTILS
+	OPT_WITH_ARG("--usb",
+		     opt_set_charp, NULL, &opt_usb_select,
+		     "USB device selection"),
+	OPT_WITH_ARG("--usb-dump",
+		     set_int_0_to_10, opt_show_intval, &opt_usbdump,
+		     opt_hidden),
+	OPT_WITHOUT_ARG("--usb-list-all",
+			opt_set_bool, &opt_usb_list_all,
+			opt_hidden),
+#endif
+	OPT_WITH_ARG("--user|-u",
+		     set_user, NULL, &opt_set_null,
+		     "Username for bitcoin JSON-RPC server"),
+	OPT_WITH_ARG("--userpass|-O",
+		     set_userpass, NULL, &opt_set_null,
+		     "Username:Password pair for bitcoin JSON-RPC server"),
+	OPT_WITHOUT_ARG("--verbose",
+			opt_set_bool, &opt_log_output,
+			"Log verbose output to stderr as well as status output"),
+	OPT_WITHOUT_ARG("--widescreen",
+			opt_set_bool, &opt_widescreen,
+			"Use extra wide display without toggling"),
+	OPT_WITHOUT_ARG("--worktime",
+			opt_set_bool, &opt_worktime,
+			"Display extra work time debug information"),
+#if defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_AVALON9) || defined(USE_AVALONLC3)
+	OPT_WITH_ARG("--force-clean-jobs",
+		     opt_set_intval, NULL, &opt_force_clean_jobs,
+		     "Force clean jobs to miners (default: 20)"),
+#endif
+	OPT_ENDTABLE
+};
+
+static char *load_config(const char *arg, void __maybe_unused *unused);
+
+static int fileconf_load;
+
+static char *parse_config(json_t *config, bool fileconf)
+{
+	static char err_buf[200];
+	struct opt_table *opt;
+	const char *str;
+	json_t *val;
+
+	if (fileconf && !fileconf_load)
+		fileconf_load = 1;
+
+	for (opt = opt_config_table; opt->type != OPT_END; opt++) {
+		char *p, *saved, *name;
+
+		/* We don't handle subtables. */
+		assert(!(opt->type & OPT_SUBTABLE));
+
+		if (!opt->names || !strlen(opt->names))
+			continue;
+
+		/* Pull apart the option name(s). */
+		name = strdup(opt->names);
+		for (p = strtok_r(name, "|", &saved); p != NULL; p = strtok_r(NULL, "|", &saved)) {
+			char *err = NULL;
+
+			if (strlen(p) < 3)
+				continue;
+
+			/* Ignore short options. */
+			if (p[1] != '-')
+				continue;
+
+			val = json_object_get(config, p+2);
+			if (!val)
+				continue;
+
+			if ((opt->type & (OPT_HASARG | OPT_PROCESSARG)) && json_is_string(val)) {
+				str = json_string_value(val);
+				err = opt->cb_arg(str, opt->u.arg);
+				if (opt->type == OPT_PROCESSARG)
+					opt_set_charp(str, opt->u.arg);
+			} else if ((opt->type & (OPT_HASARG | OPT_PROCESSARG)) && json_is_array(val)) {
+				json_t *arr_val;
+				size_t index;
+
+				json_array_foreach(val, index, arr_val) {
+					if (json_is_string(arr_val)) {
+						str = json_string_value(arr_val);
+						err = opt->cb_arg(str, opt->u.arg);
+						if (opt->type == OPT_PROCESSARG)
+							opt_set_charp(str, opt->u.arg);
+					} else if (json_is_object(arr_val))
+						err = parse_config(arr_val, false);
+					if (err)
+						break;
+				}
+			} else if ((opt->type & OPT_NOARG) && json_is_true(val))
+				err = opt->cb(opt->u.arg);
+			else
+				err = "Invalid value";
+
+			if (err) {
+				/* Allow invalid values to be in configuration
+				 * file, just skipping over them provided the
+				 * JSON is still valid after that. */
+				if (fileconf) {
+					applog(LOG_ERR, "Invalid config option %s: %s", p, err);
+					fileconf_load = -1;
+				} else {
+					snprintf(err_buf, sizeof(err_buf), "Parsing JSON option %s: %s",
+						p, err);
+					return err_buf;
+				}
+			}
+		}
+		free(name);
+	}
+
+	val = json_object_get(config, JSON_INCLUDE_CONF);
+	if (val && json_is_string(val))
+		return load_config(json_string_value(val), NULL);
+
+	return NULL;
+}
+
+char *cnfbuf = NULL;
+
+#ifdef HAVE_LIBCURL
+char conf_web1[] = "http://";
+char conf_web2[] = "https://";
+
+static char *load_web_config(const char *arg)
+{
+	json_t *val = json_web_config(arg);
+
+	if (!val || !json_is_object(val))
+		return JSON_WEB_ERROR;
+
+	if (!cnfbuf)
+		cnfbuf = strdup(arg);
+
+	config_loaded = true;
+
+	return parse_config(val, true);
+}
+#endif
+
+static char *load_config(const char *arg, void __maybe_unused *unused)
+{
+	json_error_t err;
+	json_t *config;
+	char *json_error;
+	size_t siz;
+
+#ifdef HAVE_LIBCURL
+	if (strncasecmp(arg, conf_web1, sizeof(conf_web1)-1) == 0 ||
+	    strncasecmp(arg, conf_web2, sizeof(conf_web2)-1) == 0)
+		return load_web_config(arg);
+#endif
+
+	if (!cnfbuf)
+		cnfbuf = strdup(arg);
+
+	if (++include_count > JSON_MAX_DEPTH)
+		return JSON_MAX_DEPTH_ERR;
+
+	config = json_load_file(arg, 0, &err);
+	if (!json_is_object(config)) {
+		siz = JSON_LOAD_ERROR_LEN + strlen(arg) + strlen(err.text);
+		json_error = cgmalloc(siz);
+		snprintf(json_error, siz, JSON_LOAD_ERROR, arg, err.text);
+		return json_error;
+	}
+
+	config_loaded = true;
+
+	/* Parse the config now, so we can override it.  That can keep pointers
+	 * so don't free config object. */
+	return parse_config(config, true);
+}
+
+static char *set_default_config(const char *arg)
+{
+	opt_set_charp(arg, &default_config);
+
+	return NULL;
+}
+
+void default_save_file(char *filename);
+
+static void load_default_config(void)
+{
+	cnfbuf = cgmalloc(PATH_MAX);
+
+	default_save_file(cnfbuf);
+
+	if (!access(cnfbuf, R_OK))
+		load_config(cnfbuf, NULL);
+	else {
+		free(cnfbuf);
+		cnfbuf = NULL;
+	}
+}
+
+extern const char *opt_argv0;
+
+static char *opt_verusage_and_exit(const char *extra)
+{
+	printf("%s\nBuilt with "
+#ifdef USE_ANT_S1
+		"ant.S1 "
+#endif
+#ifdef USE_ANT_S2
+#ifdef USE_ANT_S3
+		"ant.S3 "
+#else
+		"ant.S2 "
+#endif
+#endif
+#ifdef USE_AVALON
+		"avalon "
+#endif
+#ifdef USE_AVALON2
+		"avalon2 "
+#endif
+#ifdef USE_AVALON4
+		"avalon4 "
+#endif
+#ifdef USE_AVALON7
+		"avalon7 "
+#endif
+#ifdef USE_AVALON8
+		"avalon8 "
+#endif
+#ifdef USE_AVALON9
+		"avalon9 "
+#endif
+#ifdef USE_AVALONLC3
+		"avalonlc3 "
+#endif
+#ifdef USE_AVALON_MINER
+		"avalon miner"
+#endif
+#ifdef USE_BAB
+                "BaB "
+#endif
+#ifdef USE_BFLSC
+		"bflsc "
+#endif
+#ifdef USE_BITFORCE
+		"bitforce "
+#endif
+#ifdef USE_BITFURY
+		"bitfury "
+#endif
+#ifdef USE_BITFURY16
+		"bitfury16 "
+#endif
+#ifdef USE_BITMINE_A1
+                "Bitmine.A1 "
+#endif
+#ifdef USE_BLOCKERUPTER
+		"Blockerupter "
+#endif
+#ifdef USE_COINTERRA
+		"cointerra "
+#endif
+#ifdef USE_DRILLBIT
+                "drillbit "
+#endif
+#ifdef USE_DRAGONMINT_T1
+		"dragonmint_t1 "
+#endif
+#ifdef USE_GEKKO
+		"gekko "
+#endif
+#ifdef USE_HASHFAST
+		"hashfast "
+#endif
+#ifdef USE_ICARUS
+		"icarus "
+#endif
+#ifdef USE_KLONDIKE
+		"klondike "
+#endif
+#ifdef USE_KNC
+		"KnC "
+#endif
+#ifdef USE_MINION
+		"minion "
+#endif
+#ifdef USE_MODMINER
+		"modminer "
+#endif
+#ifdef USE_BITMINE_A1
+		"Bitmine.A1 "
+#endif
+#ifdef USE_SP10
+		"spondoolies "
+#endif
+#ifdef USE_SP30
+        	"sp30 "
+#endif
+#ifdef USE_XTRANONCE
+		"xnsub "
+#endif
+		"mining support.\n"
+		, packagename);
+	printf("%s", opt_usage(opt_argv0, extra));
+	fflush(stdout);
+	exit(0);
+}
+
+#if defined(USE_USBUTILS)
+char *display_devs(int *ndevs)
+{
+	*ndevs = 0;
+	usb_all(0);
+	exit(*ndevs);
+}
+#endif
+
+/* These options are available from commandline only */
+static struct opt_table opt_cmdline_table[] = {
+	OPT_WITH_ARG("--config|-c",
+		     load_config, NULL, &opt_set_null,
+		     "Load a JSON-format configuration file\n"
+		     "See example.conf for an example configuration."),
+	OPT_WITH_ARG("--default-config",
+		     set_default_config, NULL, &opt_set_null,
+		     "Specify the filename of the default config file\n"
+		     "Loaded at start and used when saving without a name."),
+	OPT_WITHOUT_ARG("--help|-h",
+			opt_verusage_and_exit, NULL,
+			"Print this message"),
+#if defined(USE_USBUTILS)
+	OPT_WITHOUT_ARG("--ndevs|-n",
+			display_devs, &nDevs,
+			"Display all USB devices and exit"),
+#endif
+	OPT_WITHOUT_ARG("--version|-V",
+			opt_version_and_exit, packagename,
+			"Display version and exit"),
+	OPT_ENDTABLE
+};
+
+static void calc_midstate(struct pool *pool, struct work *work)
+{
+	unsigned char data[64];
+	uint32_t *data32 = (uint32_t *)data;
+	sha256_ctx ctx;
+
+	if (pool->vmask) {
+		/* This would only be set if the driver requested a vmask and
+		 * the pool has a valid version mask. */
+		memcpy(work->data, &(pool->vmask_001[2]), 4);
+		flip64(data32, work->data);
+		sha256_init(&ctx);
+		sha256_update(&ctx, data, 64);
+		cg_memcpy(work->midstate1, ctx.h, 32);
+		endian_flip32(work->midstate1, work->midstate1);
+
+		memcpy(work->data, &(pool->vmask_001[4]), 4);
+		flip64(data32, work->data);
+		sha256_init(&ctx);
+		sha256_update(&ctx, data, 64);
+		cg_memcpy(work->midstate2, ctx.h, 32);
+		endian_flip32(work->midstate2, work->midstate2);
+
+		memcpy(work->data, &(pool->vmask_001[8]), 4);
+		flip64(data32, work->data);
+		sha256_init(&ctx);
+		sha256_update(&ctx, data, 64);
+		cg_memcpy(work->midstate3, ctx.h, 32);
+		endian_flip32(work->midstate3, work->midstate3);
+
+		memcpy(work->data, &(pool->vmask_001[0]), 4);
+	}
+	flip64(data32, work->data);
+	sha256_init(&ctx);
+	sha256_update(&ctx, data, 64);
+	cg_memcpy(work->midstate, ctx.h, 32);
+	endian_flip32(work->midstate, work->midstate);
+}
+
+/* Returns the current value of total_work and increments it */
+static int total_work_inc(void)
+{
+	int ret;
+
+	cg_wlock(&control_lock);
+	ret = total_work++;
+	cg_wunlock(&control_lock);
+
+	return ret;
+}
+
+static struct work *make_work(void)
+{
+	struct work *work = cgcalloc(1, sizeof(struct work));
+
+	work->id = total_work_inc();
+	return work;
+}
+
+/* This is the central place all work that is about to be retired should be
+ * cleaned to remove any dynamically allocated arrays within the struct */
+void clean_work(struct work *work)
+{
+	free(work->job_id);
+	free(work->ntime);
+	free(work->coinbase);
+	free(work->nonce1);
+	memset(work, 0, sizeof(struct work));
+}
+
+/* All dynamically allocated work structs should be freed here to not leak any
+ * ram from arrays allocated within the work struct. Null the actual pointer
+ * used to call free_work. */
+void _free_work(struct work **workptr, const char *file, const char *func, const int line)
+{
+	struct work *work = *workptr;
+
+	if (unlikely(!work)) {
+		applog(LOG_ERR, "Free work called with null work from %s %s:%d",
+		       file, func, line);
+		return;
+	}
+
+	clean_work(work);
+	free(work);
+	*workptr = NULL;
+}
+
+static void gen_hash(unsigned char *data, unsigned char *hash, int len);
+static void calc_diff(struct work *work, double known);
+char *workpadding = "000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000";
+
+#ifdef HAVE_LIBCURL
+/* Process transactions with GBT by storing the binary value of the first
+ * transaction, and the hashes of the remaining transactions since these
+ * remain constant with an altered coinbase when generating work. Must be
+ * entered under gbt_lock */
+static void gbt_merkle_bins(struct pool *pool, json_t *transaction_arr);
+
+static void __build_gbt_txns(struct pool *pool, json_t *res_val)
+{
+	json_t *txn_array;
+
+	txn_array = json_object_get(res_val, "transactions");
+	gbt_merkle_bins(pool, txn_array);
+}
+
+static void __gbt_merkleroot(struct pool *pool, unsigned char *merkle_root)
+{
+	unsigned char merkle_sha[64];
+	int i;
+
+	gen_hash(pool->coinbase, merkle_root, pool->coinbase_len);
+	cg_memcpy(merkle_sha, merkle_root, 32);
+	for (i = 0; i < pool->merkles; i++) {
+		cg_memcpy(merkle_sha + 32, pool->merklebin + i * 32, 32);
+		gen_hash(merkle_sha, merkle_root, 64);
+		cg_memcpy(merkle_sha, merkle_root, 32);
+	}
+}
+
+static bool work_decode(struct pool *pool, struct work *work, json_t *val);
+
+static void update_gbt(struct pool *pool)
+{
+	int rolltime;
+	json_t *val;
+	CURL *curl;
+
+	curl = curl_easy_init();
+	if (unlikely(!curl))
+		quit (1, "CURL initialisation failed in update_gbt");
+
+	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass,
+			    pool->rpc_req, true, false, &rolltime, pool, false);
+
+	if (val) {
+		struct work *work = make_work();
+		bool rc = work_decode(pool, work, val);
+
+		total_getworks++;
+		pool->getwork_requested++;
+		if (rc) {
+			applog(LOG_DEBUG, "Successfully retrieved and updated GBT from pool %u %s",
+			       pool->pool_no, pool->rpc_url);
+			if (pool == current_pool())
+				opt_work_update = true;
+		} else {
+			applog(LOG_DEBUG, "Successfully retrieved but FAILED to decipher GBT from pool %u %s",
+			       pool->pool_no, pool->rpc_url);
+		}
+		json_decref(val);
+		free_work(work);
+	} else {
+		applog(LOG_DEBUG, "FAILED to update GBT from pool %u %s",
+		       pool->pool_no, pool->rpc_url);
+	}
+	curl_easy_cleanup(curl);
+}
+
+static void gen_gbt_work(struct pool *pool, struct work *work)
+{
+	unsigned char merkleroot[32];
+	struct timeval now;
+	uint64_t nonce2le;
+
+	cgtime(&now);
+	if (now.tv_sec - pool->tv_lastwork.tv_sec > 60)
+		update_gbt(pool);
+
+	cg_wlock(&pool->gbt_lock);
+	nonce2le = htole64(pool->nonce2);
+	cg_memcpy(pool->coinbase + pool->nonce2_offset, &nonce2le, pool->n2size);
+	pool->nonce2++;
+	cg_dwlock(&pool->gbt_lock);
+	__gbt_merkleroot(pool, merkleroot);
+
+	cg_memcpy(work->data, &pool->gbt_version, 4);
+	cg_memcpy(work->data + 4, pool->previousblockhash, 32);
+	cg_memcpy(work->data + 4 + 32 + 32, &pool->curtime, 4);
+	cg_memcpy(work->data + 4 + 32 + 32 + 4, &pool->gbt_bits, 4);
+
+	cg_memcpy(work->target, pool->gbt_target, 32);
+
+	work->coinbase = bin2hex(pool->coinbase, pool->coinbase_len);
+
+	/* For encoding the block data on submission */
+	work->gbt_txns = pool->gbt_txns + 1;
+
+	if (pool->gbt_workid)
+		work->job_id = strdup(pool->gbt_workid);
+	cg_runlock(&pool->gbt_lock);
+
+	flip32(work->data + 4 + 32, merkleroot);
+	memset(work->data + 4 + 32 + 32 + 4 + 4, 0, 4); /* nonce */
+
+	hex2bin(work->data + 4 + 32 + 32 + 4 + 4 + 4, workpadding, 48);
+
+	if (opt_debug) {
+		char *header = bin2hex(work->data, 128);
+
+		applog(LOG_DEBUG, "Generated GBT header %s", header);
+		applog(LOG_DEBUG, "Work coinbase %s", work->coinbase);
+		free(header);
+	}
+
+	calc_midstate(pool, work);
+	local_work++;
+	work->pool = pool;
+	work->gbt = true;
+	work->longpoll = false;
+	work->getwork_mode = GETWORK_MODE_GBT;
+	work->work_block = work_block;
+	/* Nominally allow a driver to ntime roll 60 seconds */
+	work->drv_rolllimit = 60;
+	calc_diff(work, 0);
+	cgtime(&work->tv_staged);
+}
+
+static bool gbt_decode(struct pool *pool, json_t *res_val)
+{
+	const char *previousblockhash;
+	const char *target;
+	const char *coinbasetxn;
+	const char *longpollid;
+	unsigned char hash_swap[32];
+	int expires;
+	int version;
+	int curtime;
+	bool submitold;
+	const char *bits;
+	const char *workid;
+	int cbt_len, orig_len;
+	uint8_t *extra_len;
+	size_t cal_len;
+
+	previousblockhash = json_string_value(json_object_get(res_val, "previousblockhash"));
+	target = json_string_value(json_object_get(res_val, "target"));
+	coinbasetxn = json_string_value(json_object_get(json_object_get(res_val, "coinbasetxn"), "data"));
+	longpollid = json_string_value(json_object_get(res_val, "longpollid"));
+	expires = json_integer_value(json_object_get(res_val, "expires"));
+	version = json_integer_value(json_object_get(res_val, "version"));
+	curtime = json_integer_value(json_object_get(res_val, "curtime"));
+	submitold = json_is_true(json_object_get(res_val, "submitold"));
+	bits = json_string_value(json_object_get(res_val, "bits"));
+	workid = json_string_value(json_object_get(res_val, "workid"));
+
+	if (!previousblockhash || !target || !coinbasetxn || !longpollid ||
+	    !expires || !version || !curtime || !bits) {
+		applog(LOG_ERR, "JSON failed to decode GBT");
+		return false;
+	}
+
+	applog(LOG_DEBUG, "previousblockhash: %s", previousblockhash);
+	applog(LOG_DEBUG, "target: %s", target);
+	applog(LOG_DEBUG, "coinbasetxn: %s", coinbasetxn);
+	applog(LOG_DEBUG, "longpollid: %s", longpollid);
+	applog(LOG_DEBUG, "expires: %d", expires);
+	applog(LOG_DEBUG, "version: %d", version);
+	applog(LOG_DEBUG, "curtime: %d", curtime);
+	applog(LOG_DEBUG, "submitold: %s", submitold ? "true" : "false");
+	applog(LOG_DEBUG, "bits: %s", bits);
+	if (workid)
+		applog(LOG_DEBUG, "workid: %s", workid);
+
+	cg_wlock(&pool->gbt_lock);
+	free(pool->coinbasetxn);
+	pool->coinbasetxn = strdup(coinbasetxn);
+	cbt_len = strlen(pool->coinbasetxn) / 2;
+	/* We add 8 bytes of extra data corresponding to nonce2 */
+	pool->n2size = 8;
+	pool->coinbase_len = cbt_len + pool->n2size;
+	cal_len = pool->coinbase_len + 1;
+	free(pool->coinbase);
+	pool->coinbase = cgcalloc(cal_len, 1);
+	hex2bin(pool->coinbase, pool->coinbasetxn, 42);
+	extra_len = (uint8_t *)(pool->coinbase + 41);
+	orig_len = *extra_len;
+	hex2bin(pool->coinbase + 42, pool->coinbasetxn + 84, orig_len);
+	*extra_len += pool->n2size;
+	hex2bin(pool->coinbase + 42 + *extra_len, pool->coinbasetxn + 84 + (orig_len * 2),
+		cbt_len - orig_len - 42);
+	pool->nonce2_offset = orig_len + 42;
+
+	free(pool->longpollid);
+	pool->longpollid = strdup(longpollid);
+	free(pool->gbt_workid);
+	if (workid)
+		pool->gbt_workid = strdup(workid);
+	else
+		pool->gbt_workid = NULL;
+
+	hex2bin(hash_swap, previousblockhash, 32);
+	swap256(pool->previousblockhash, hash_swap);
+
+	hex2bin(hash_swap, target, 32);
+	swab256(pool->gbt_target, hash_swap);
+
+	pool->gbt_expires = expires;
+	pool->gbt_version = htobe32(version);
+	pool->curtime = htobe32(curtime);
+	pool->submit_old = submitold;
+
+	hex2bin((unsigned char *)&pool->gbt_bits, bits, 4);
+
+	__build_gbt_txns(pool, res_val);
+	if (pool->transactions < 3)
+		pool->bad_work++;
+	cg_wunlock(&pool->gbt_lock);
+
+	return true;
+}
+
+static void gbt_merkle_bins(struct pool *pool, json_t *transaction_arr)
+{
+	unsigned char *hashbin;
+	json_t *arr_val;
+	int i, j, binleft, binlen;
+
+	free(pool->txn_data);
+	pool->txn_data = NULL;
+	pool->transactions = 0;
+	pool->merkles = 0;
+	pool->transactions = json_array_size(transaction_arr);
+	binlen = pool->transactions * 32 + 32;
+	hashbin = alloca(binlen + 32);
+	memset(hashbin, 0, 32);
+	binleft = binlen / 32;
+	if (pool->transactions) {
+		int len = 0, ofs = 0;
+		const char *txn;
+
+		for (i = 0; i < pool->transactions; i++) {
+			arr_val = json_array_get(transaction_arr, i);
+			txn = json_string_value(json_object_get(arr_val, "data"));
+			if (!txn) {
+				applog(LOG_ERR, "Pool %d json_string_value fail - cannot find transaction data",
+					pool->pool_no);
+				return;
+			}
+			len += strlen(txn);
+		}
+
+		pool->txn_data = cgmalloc(len + 1);
+		pool->txn_data[len] = '\0';
+
+		for (i = 0; i < pool->transactions; i++) {
+			unsigned char binswap[32];
+			const char *hash;
+			const char *txid;
+
+			arr_val = json_array_get(transaction_arr, i);
+			txid = json_string_value(json_object_get(arr_val, "txid"));
+			hash = json_string_value(json_object_get(arr_val, "hash"));
+			if(!txid)
+				txid = hash;
+			txn = json_string_value(json_object_get(arr_val, "data"));
+			len = strlen(txn);
+			cg_memcpy(pool->txn_data + ofs, txn, len);
+			ofs += len;
+#if 0
+			// This logic no longer works post-segwit. The txids will always need to be sent,
+			// as the full txns will also contain witness data that must be omitted in these
+			// hashes.
+			if (!hash) {
+				unsigned char *txn_bin;
+				int txn_len;
+
+				txn_len = len / 2;
+				txn_bin = cgmalloc(txn_len);
+				hex2bin(txn_bin, txn, txn_len);
+				/* This is needed for pooled mining since only
+				 * transaction data and not hashes are sent */
+				gen_hash(txn_bin, hashbin + 32 + 32 * i, txn_len);
+				continue;
+			}
+#endif
+			if (!txid) {
+				applog(LOG_ERR, "missing txid in gbt_merkle_bins");
+				return;
+			}
+			if (!hex2bin(binswap, txid, 32)) {
+				applog(LOG_ERR, "Failed to hex2bin txid in gbt_merkle_bins");
+				return;
+			}
+			swab256(hashbin + 32 + 32 * i, binswap);
+		}
+	}
+	if (binleft > 1) {
+		while (42) {
+			if (binleft == 1)
+				break;
+			cg_memcpy(pool->merklebin + (pool->merkles * 32), hashbin + 32, 32);
+			pool->merkles++;
+			if (binleft % 2) {
+				cg_memcpy(hashbin + binlen, hashbin + binlen - 32, 32);
+				binlen += 32;
+				binleft++;
+			}
+			for (i = 32, j = 64; j < binlen; i += 32, j += 64) {
+				gen_hash(hashbin + j, hashbin + i, 64);
+			}
+			binleft /= 2;
+			binlen = binleft * 32;
+		}
+	}
+	if (opt_debug) {
+		char hashhex[68];
+
+		for (i = 0; i < pool->merkles; i++) {
+			__bin2hex(hashhex, pool->merklebin + i * 32, 32);
+			applog(LOG_DEBUG, "MH%d %s",i, hashhex);
+		}
+	}
+	applog(LOG_INFO, "Stored %d transactions from pool %d", pool->transactions,
+		pool->pool_no);
+}
+
+static const unsigned char witness_nonce[32] = {0};
+static const int witness_nonce_size = sizeof(witness_nonce);
+static const unsigned char witness_header[] = {0xaa, 0x21, 0xa9, 0xed};
+static const int witness_header_size = sizeof(witness_header);
+
+static bool gbt_witness_data(json_t *transaction_arr, unsigned char* witnessdata, int avail_size)
+{
+	int i, binlen, txncount = json_array_size(transaction_arr);
+	unsigned char *hashbin;
+	const char *hash;
+	json_t *arr_val;
+
+	binlen = txncount * 32 + 32;
+	hashbin = alloca(binlen + 32);
+	memset(hashbin, 0, 32);
+
+	if (avail_size < witness_header_size + 32)
+		return false;
+
+	for (i = 0; i < txncount; i++) {
+		unsigned char binswap[32];
+
+		arr_val = json_array_get(transaction_arr, i);
+		hash = json_string_value(json_object_get(arr_val, "hash"));
+		if (unlikely(!hash)) {
+			applog(LOG_ERR, "Hash missing for transaction");
+			return false;
+		}
+		if (!hex2bin(binswap, hash, 32)) {
+			applog(LOG_ERR, "Failed to hex2bin hash in gbt_witness_data");
+			return false;
+		}
+		swab256(hashbin + 32 + 32 * i, binswap);
+	}
+
+	// Build merkle root (copied from libblkmaker)
+	for (txncount++ ; txncount > 1 ; txncount /= 2) {
+		if (txncount % 2) {
+			// Odd number, duplicate the last
+			memcpy(hashbin + 32 * txncount, hashbin + 32 * (txncount - 1), 32);
+			txncount++;
+		}
+		for (i = 0; i < txncount; i += 2) {
+			// We overlap input and output here, on the first pair
+			gen_hash(hashbin + 32 * i, hashbin + 32 * (i / 2), 64);
+		}
+	}
+
+	memcpy(witnessdata, witness_header, witness_header_size);
+	memcpy(hashbin + 32, &witness_nonce, witness_nonce_size);
+	gen_hash(hashbin, witnessdata + witness_header_size, 32 + witness_nonce_size);
+	return true;
+}
+
+static double diff_from_target(void *target);
+
+static const char scriptsig_header[] = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff";
+static unsigned char scriptsig_header_bin[41];
+
+static bool gbt_solo_decode(struct pool *pool, json_t *res_val)
+{
+	json_t *transaction_arr, *rules_arr, *coinbase_aux;
+	const char *previousblockhash;
+	unsigned char hash_swap[32];
+	struct timeval now;
+	const char *target;
+	uint64_t coinbasevalue;
+	const char *flags;
+	const char *bits;
+	char header[260];
+	int ofs = 0, len;
+	uint64_t *u64;
+	uint32_t *u32;
+	int version;
+	int curtime;
+	int height;
+	int witness_txout_len = 0;
+	int witnessdata_size = 0;
+	bool insert_witness = false;
+	unsigned char witnessdata[36] = {};
+	const char *default_witness_commitment;
+
+	previousblockhash = json_string_value(json_object_get(res_val, "previousblockhash"));
+	target = json_string_value(json_object_get(res_val, "target"));
+	transaction_arr = json_object_get(res_val, "transactions");
+	rules_arr = json_object_get(res_val, "rules");
+	version = json_integer_value(json_object_get(res_val, "version"));
+	curtime = json_integer_value(json_object_get(res_val, "curtime"));
+	bits = json_string_value(json_object_get(res_val, "bits"));
+	height = json_integer_value(json_object_get(res_val, "height"));
+	coinbasevalue = json_integer_value(json_object_get(res_val, "coinbasevalue"));
+	coinbase_aux = json_object_get(res_val, "coinbaseaux");
+	flags = json_string_value(json_object_get(coinbase_aux, "flags"));
+	default_witness_commitment = json_string_value(json_object_get(res_val, "default_witness_commitment"));
+
+	if (!previousblockhash || !target || !version || !curtime || !bits || !coinbase_aux || !flags) {
+		applog(LOG_ERR, "Pool %d JSON failed to decode GBT", pool->pool_no);
+		return false;
+	}
+
+	if (rules_arr) {
+		int i;
+		int rule_count = json_array_size(rules_arr);
+		const char *rule;
+
+		for (i = 0; i < rule_count; i++) {
+			rule = json_string_value(json_array_get(rules_arr, i));
+			if (!rule)
+				continue;
+			if (*rule == '!')
+				rule++;
+			if (strncmp(rule, "segwit", 6)) {
+				insert_witness = true;
+				break;
+			}
+		}
+	}
+
+
+	applog(LOG_DEBUG, "previousblockhash: %s", previousblockhash);
+	applog(LOG_DEBUG, "target: %s", target);
+	applog(LOG_DEBUG, "version: %d", version);
+	applog(LOG_DEBUG, "curtime: %d", curtime);
+	applog(LOG_DEBUG, "bits: %s", bits);
+	applog(LOG_DEBUG, "height: %d", height);
+	applog(LOG_DEBUG, "flags: %s", flags);
+
+	cg_wlock(&pool->gbt_lock);
+	hex2bin(hash_swap, previousblockhash, 32);
+	swap256(pool->previousblockhash, hash_swap);
+	__bin2hex(pool->prev_hash, pool->previousblockhash, 32);
+
+	hex2bin(hash_swap, target, 32);
+	swab256(pool->gbt_target, hash_swap);
+	pool->sdiff = diff_from_target(pool->gbt_target);
+
+	pool->gbt_version = htobe32(version);
+	pool->curtime = htobe32(curtime);
+	snprintf(pool->ntime, 9, "%08x", curtime);
+	snprintf(pool->bbversion, 9, "%08x", version);
+	snprintf(pool->nbit, 9, "%s", bits);
+	pool->nValue = coinbasevalue;
+	hex2bin((unsigned char *)&pool->gbt_bits, bits, 4);
+	gbt_merkle_bins(pool, transaction_arr);
+
+	if (insert_witness) {
+		char witness_str[sizeof(witnessdata) * 2];
+
+		witnessdata_size = sizeof(witnessdata);
+		if (!gbt_witness_data(transaction_arr, witnessdata, witnessdata_size)) {
+			applog(LOG_ERR, "error calculating witness data");
+			return false;
+		}
+		__bin2hex(witness_str, witnessdata, witnessdata_size);
+		applog(LOG_DEBUG, "calculated witness data: %s", witness_str);
+		if (default_witness_commitment) {
+			if (strncmp(witness_str, default_witness_commitment + 4, witnessdata_size * 2) != 0) {
+				applog(LOG_ERR, "bad witness data. %s != %s", default_witness_commitment + 4, witness_str);
+				return false;
+			}
+		}
+	}
+
+	if (pool->transactions < 3)
+		pool->bad_work++;
+	pool->height = height;
+
+	memset(pool->scriptsig_base, 0, 42);
+	ofs++; // Leave room for template length
+
+	/* Put block height at start of template. */
+	ofs += ser_number(pool->scriptsig_base + ofs, height); // max 5
+
+	/* Followed by flags */
+	if (flags) {
+		len = strlen(flags) / 2;
+		pool->scriptsig_base[ofs++] = len;
+		hex2bin(pool->scriptsig_base + ofs, flags, len);
+		ofs += len;
+	}
+
+	/* Followed by timestamp */
+	cgtime(&now);
+	pool->scriptsig_base[ofs++] = 0xfe; // Encode seconds as u32
+	u32 = (uint32_t *)&pool->scriptsig_base[ofs];
+	*u32 = htole32(now.tv_sec);
+	ofs += 4; // sizeof uint32_t
+	pool->scriptsig_base[ofs++] = 0xfe; // Encode usecs as u32
+	u32 = (uint32_t *)&pool->scriptsig_base[ofs];
+	*u32 = htole32(now.tv_usec);
+	ofs += 4; // sizeof uint32_t
+
+	cg_memcpy(pool->scriptsig_base + ofs, "\x09\x63\x67\x6d\x69\x6e\x65\x72\x34\x32", 10);
+	ofs += 10;
+
+	/* Followed by extranonce size, fixed at 8 */
+	pool->scriptsig_base[ofs++] = 8;
+	pool->nonce2_offset = 41 + ofs;
+	ofs += 8;
+
+	if (opt_btc_sig) {
+		len = strlen(opt_btc_sig);
+		if (len > 32)
+			len = 32;
+		pool->scriptsig_base[ofs++] = len;
+		cg_memcpy(pool->scriptsig_base + ofs, opt_btc_sig, len);
+		ofs += len;
+	}
+
+	pool->scriptsig_base[0] = ofs++; // Template length
+	pool->n1_len = ofs;
+
+	len = 	41 // prefix
+		+ ofs // Template length
+		+ 4 // txin sequence no
+		+ 1 // txouts
+		+ 8 // value
+		+ 1 + 25 // txout
+		+ 4; // lock
+
+	if (insert_witness) {
+		len +=  8 //value
+			+   1 + 2 + witnessdata_size; // total scriptPubKey size + OP_RETURN + push size + data
+	}
+
+	free(pool->coinbase);
+	pool->coinbase = cgcalloc(len, 1);
+	cg_memcpy(pool->coinbase + 41, pool->scriptsig_base, ofs);
+	cg_memcpy(pool->coinbase + 41 + ofs, "\xff\xff\xff\xff", 4);
+	pool->coinbase[41 + ofs + 4] = insert_witness ? 2 : 1;
+	u64 = (uint64_t *)&(pool->coinbase[41 + ofs + 4 + 1]);
+	*u64 = htole64(coinbasevalue);
+
+	if (insert_witness) {
+		unsigned char *witness = &pool->coinbase[41 + ofs + 4 + 1 + 8 + 1 + 25];
+
+		memset(witness, 0, 8);
+		witness_txout_len += 8;
+		witness[witness_txout_len++] = witnessdata_size + 2; // total scriptPubKey size
+		witness[witness_txout_len++] = 0x6a; // OP_RETURN
+		witness[witness_txout_len++] = witnessdata_size;
+		memcpy(&witness[witness_txout_len], witnessdata, witnessdata_size);
+		witness_txout_len += witnessdata_size;
+	}
+
+	pool->nonce2 = 0;
+	pool->n2size = 4;
+	pool->coinbase_len = 41 + ofs + 4 + 1 + 8 + 1 + 25 + witness_txout_len + 4;
+	cg_wunlock(&pool->gbt_lock);
+
+	snprintf(header, 257, "%s%s%s%s%s%s%s",
+		 pool->bbversion,
+		 pool->prev_hash,
+		 "0000000000000000000000000000000000000000000000000000000000000000",
+		 pool->ntime,
+		 pool->nbit,
+		 "00000000", /* nonce */
+		 workpadding);
+	if (unlikely(!hex2bin(pool->header_bin, header, 128)))
+		quit(1, "Failed to hex2bin header in gbt_solo_decode");
+
+	return true;
+}
+
+static bool work_decode(struct pool *pool, struct work *work, json_t *val)
+{
+	json_t *res_val = json_object_get(val, "result");
+	bool ret = false;
+
+	cgtime(&pool->tv_lastwork);
+	if (!res_val || json_is_null(res_val)) {
+		applog(LOG_ERR, "JSON Failed to decode result");
+		goto out;
+	}
+
+	if (pool->gbt_solo) {
+		if (unlikely(!gbt_solo_decode(pool, res_val)))
+			goto out;
+		goto out_true;
+	}
+	if (unlikely(!gbt_decode(pool, res_val)))
+		goto out;
+	work->gbt = true;
+	memset(work->hash, 0, sizeof(work->hash));
+
+	cgtime(&work->tv_staged);
+out_true:
+	ret = true;
+out:
+	return ret;
+}
+#else /* HAVE_LIBCURL */
+#define json_rpc_call(curl, url, userpass, rpc_req, probe, longpoll, rolltime, pool, share) (NULL)
+#define work_decode(pool, work, val) (false)
+#define gen_gbt_work(pool, work) {}
+#endif /* HAVE_LIBCURL */
+
+int dev_from_id(int thr_id)
+{
+	struct cgpu_info *cgpu = get_thr_cgpu(thr_id);
+
+	return cgpu->device_id;
+}
+
+/* Create an exponentially decaying average over the opt_log_interval */
+void decay_time(double *f, double fadd, double fsecs, double interval)
+{
+	double ftotal, fprop;
+
+	if (fsecs <= 0)
+		return;
+	fprop = 1.0 - 1 / (exp(fsecs / interval));
+	ftotal = 1.0 + fprop;
+	*f += (fadd / fsecs * fprop);
+	*f /= ftotal;
+}
+
+static int __total_staged(void)
+{
+	return HASH_COUNT(staged_work);
+}
+#if defined(HAVE_LIBCURL) || defined(HAVE_CURSES)
+static int total_staged(void)
+{
+	int ret;
+
+	mutex_lock(stgd_lock);
+	ret = __total_staged();
+	mutex_unlock(stgd_lock);
+
+	return ret;
+}
+#endif
+
+#ifdef HAVE_CURSES
+WINDOW *mainwin, *statuswin, *logwin;
+#endif
+double total_secs = 1.0;
+static char statusline[256];
+/* logstart is where the log window should start */
+static int devcursor, logstart, logcursor;
+#ifdef HAVE_CURSES
+/* statusy is where the status window goes up to in cases where it won't fit at startup */
+static int statusy;
+#endif
+
+#ifdef HAVE_CURSES
+static inline void unlock_curses(void)
+{
+	mutex_unlock(&console_lock);
+}
+
+static inline void lock_curses(void)
+{
+	mutex_lock(&console_lock);
+}
+
+static bool curses_active_locked(void)
+{
+	bool ret;
+
+	lock_curses();
+	ret = curses_active;
+	if (!ret)
+		unlock_curses();
+	return ret;
+}
+#endif
+
+/* Convert a uint64_t value into a truncated string for displaying with its
+ * associated suitable for Mega, Giga etc. Buf array needs to be long enough */
+static void suffix_string(uint64_t val, char *buf, size_t bufsiz, int sigdigits)
+{
+	const double  dkilo = 1000.0;
+	const uint64_t kilo = 1000ull;
+	const uint64_t mega = 1000000ull;
+	const uint64_t giga = 1000000000ull;
+	const uint64_t tera = 1000000000000ull;
+	const uint64_t peta = 1000000000000000ull;
+	const uint64_t exa  = 1000000000000000000ull;
+	char suffix[2] = "";
+	bool decimal = true;
+	double dval;
+
+	if (val >= exa) {
+		val /= peta;
+		dval = (double)val / dkilo;
+		strcpy(suffix, "E");
+	} else if (val >= peta) {
+		val /= tera;
+		dval = (double)val / dkilo;
+		strcpy(suffix, "P");
+	} else if (val >= tera) {
+		val /= giga;
+		dval = (double)val / dkilo;
+		strcpy(suffix, "T");
+	} else if (val >= giga) {
+		val /= mega;
+		dval = (double)val / dkilo;
+		strcpy(suffix, "G");
+	} else if (val >= mega) {
+		val /= kilo;
+		dval = (double)val / dkilo;
+		strcpy(suffix, "M");
+	} else if (val >= kilo) {
+		dval = (double)val / dkilo;
+		strcpy(suffix, "K");
+	} else {
+		dval = val;
+		decimal = false;
+	}
+
+	if (!sigdigits) {
+		if (decimal)
+			snprintf(buf, bufsiz, "%.3g%s", dval, suffix);
+		else
+			snprintf(buf, bufsiz, "%d%s", (unsigned int)dval, suffix);
+	} else {
+		/* Always show sigdigits + 1, padded on right with zeroes
+		 * followed by suffix */
+		int ndigits = sigdigits - 1 - (dval > 0.0 ? floor(log10(dval)) : 0);
+
+		snprintf(buf, bufsiz, "%*.*f%s", sigdigits + 1, ndigits, dval, suffix);
+	}
+}
+
+double cgpu_runtime(struct cgpu_info *cgpu)
+{
+	struct timeval now;
+	double dev_runtime;
+
+	if (cgpu->dev_start_tv.tv_sec == 0)
+		dev_runtime = total_secs;
+	else {
+		cgtime(&now);
+		dev_runtime = tdiff(&now, &(cgpu->dev_start_tv));
+	}
+
+	if (dev_runtime < 1.0)
+		dev_runtime = 1.0;
+	return dev_runtime;
+}
+
+double tsince_restart(void)
+{
+	struct timeval now;
+
+	cgtime(&now);
+	return tdiff(&now, &restart_tv_start);
+}
+
+double tsince_update(void)
+{
+	struct timeval now;
+
+	cgtime(&now);
+	return tdiff(&now, &update_tv_start);
+}
+
+static void get_statline(char *buf, size_t bufsiz, struct cgpu_info *cgpu)
+{
+	char displayed_hashes[16], displayed_rolling[16];
+	double dev_runtime, wu;
+	uint64_t dh64, dr64;
+
+	dev_runtime = cgpu_runtime(cgpu);
+
+	wu = cgpu->diff1 / dev_runtime * 60.0;
+
+	dh64 = (double)cgpu->total_mhashes / dev_runtime * 1000000ull;
+	dr64 = (double)cgpu->rolling * 1000000ull;
+	suffix_string(dh64, displayed_hashes, sizeof(displayed_hashes), 4);
+	suffix_string(dr64, displayed_rolling, sizeof(displayed_rolling), 4);
+
+	snprintf(buf, bufsiz, "%s %d ", cgpu->drv->name, cgpu->device_id);
+	cgpu->drv->get_statline_before(buf, bufsiz, cgpu);
+	tailsprintf(buf, bufsiz, "(%ds):%s (avg):%sh/s | A:%.0f R:%.0f HW:%d WU:%.1f/m",
+		opt_log_interval,
+		displayed_rolling,
+		displayed_hashes,
+		cgpu->diff_accepted,
+		cgpu->diff_rejected,
+		cgpu->hw_errors,
+		wu);
+	cgpu->drv->get_statline(buf, bufsiz, cgpu);
+}
+
+static bool shared_strategy(void)
+{
+	return (pool_strategy == POOL_LOADBALANCE || pool_strategy == POOL_BALANCE);
+}
+
+#ifdef HAVE_CURSES
+#define CURBUFSIZ 256
+#define cg_mvwprintw(win, y, x, fmt, ...) do { \
+	char tmp42[CURBUFSIZ]; \
+	snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \
+	mvwprintw(win, y, x, "%s", tmp42); \
+} while (0)
+#define cg_wprintw(win, fmt, ...) do { \
+	char tmp42[CURBUFSIZ]; \
+	snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \
+	wprintw(win, "%s", tmp42); \
+} while (0)
+
+/* Must be called with curses mutex lock held and curses_active */
+static void curses_print_status(void)
+{
+	struct pool *pool = current_pool();
+	int linewidth = opt_widescreen ? 100 : 80;
+
+	wattron(statuswin, A_BOLD);
+	cg_mvwprintw(statuswin, 0, 0, " " PACKAGE " version " VERSION " - Started: %s", datestamp);
+	wattroff(statuswin, A_BOLD);
+	mvwhline(statuswin, 1, 0, '-', linewidth);
+	cg_mvwprintw(statuswin, 2, 0, " %s", statusline);
+	wclrtoeol(statuswin);
+	if (opt_widescreen) {
+		cg_mvwprintw(statuswin, 3, 0, " A:%.0f  R:%.0f  HW:%d  WU:%.1f/m |"
+			     " ST: %d  SS: %"PRId64"  NB: %d  LW: %d  GF: %d  RF: %d",
+			     total_diff_accepted, total_diff_rejected, hw_errors,
+			     total_diff1 / total_secs * 60,
+			     total_staged(), total_stale, new_blocks, local_work, total_go, total_ro);
+	} else if (alt_status) {
+		cg_mvwprintw(statuswin, 3, 0, " ST: %d  SS: %"PRId64"  NB: %d  LW: %d  GF: %d  RF: %d",
+			     total_staged(), total_stale, new_blocks, local_work, total_go, total_ro);
+	} else {
+		cg_mvwprintw(statuswin, 3, 0, " A:%.0f  R:%.0f  HW:%d  WU:%.1f/m",
+			     total_diff_accepted, total_diff_rejected, hw_errors,
+			     total_diff1 / total_secs * 60);
+	}
+	wclrtoeol(statuswin);
+	if (shared_strategy() && total_pools > 1) {
+		cg_mvwprintw(statuswin, 4, 0, " Connected to multiple pools with%s block change notify",
+			have_longpoll ? "": "out");
+	} else if (pool->has_stratum) {
+		cg_mvwprintw(statuswin, 4, 0, " Connected to %s diff %s with stratum as user %s",
+			pool->sockaddr_url, pool->diff, pool->rpc_user);
+	} else {
+		cg_mvwprintw(statuswin, 4, 0, " Connected to %s diff %s with%s %s as user %s",
+			pool->sockaddr_url, pool->diff, have_longpoll ? "": "out",
+			pool->has_gbt ? "GBT" : "LP", pool->rpc_user);
+	}
+	wclrtoeol(statuswin);
+	cg_mvwprintw(statuswin, 5, 0, " Block: %s...  Diff:%s  Started: %s  Best share: %s   ",
+		     prev_block, block_diff, blocktime, best_share);
+	mvwhline(statuswin, 6, 0, '-', linewidth);
+	mvwhline(statuswin, statusy - 1, 0, '-', linewidth);
+#ifdef USE_USBUTILS
+	cg_mvwprintw(statuswin, devcursor - 1, 1, "[U]SB management [P]ool management [S]ettings [D]isplay options [Q]uit");
+#else
+	cg_mvwprintw(statuswin, devcursor - 1, 1, "[P]ool management [S]ettings [D]isplay options [Q]uit");
+#endif
+}
+
+static void adj_width(int var, int *length)
+{
+	if ((int)(log10(var) + 1) > *length)
+		(*length)++;
+}
+
+static void adj_fwidth(float var, int *length)
+{
+	if ((int)(log10(var) + 1) > *length)
+		(*length)++;
+}
+
+#define STATBEFORELEN 23
+const char blanks[] = "                                        ";
+
+static void curses_print_devstatus(struct cgpu_info *cgpu, int devno, int count)
+{
+	static int devno_width = 1, dawidth = 1, drwidth = 1, hwwidth = 1, wuwidth = 1;
+	char logline[256], unique_id[12];
+	struct timeval now;
+	double dev_runtime, wu;
+	unsigned int devstatlen;
+
+	if (opt_compact)
+		return;
+
+	if (devcursor + count > LINES - 2)
+		return;
+
+	if (count >= most_devices)
+		return;
+
+	if (cgpu->dev_start_tv.tv_sec == 0)
+		dev_runtime = total_secs;
+	else {
+		cgtime(&now);
+		dev_runtime = tdiff(&now, &(cgpu->dev_start_tv));
+	}
+
+	if (dev_runtime < 1.0)
+		dev_runtime = 1.0;
+
+	cgpu->utility = cgpu->accepted / dev_runtime * 60;
+	wu = cgpu->diff1 / dev_runtime * 60;
+
+	wmove(statuswin,devcursor + count, 0);
+	adj_width(devno, &devno_width);
+	if (cgpu->unique_id) {
+		unique_id[8] = '\0';
+		cg_memcpy(unique_id, blanks, 8);
+		strncpy(unique_id, cgpu->unique_id, 8);
+	} else
+		sprintf(unique_id, "%-8d", cgpu->device_id);
+	cg_wprintw(statuswin, " %*d: %s %-8s: ", devno_width, devno, cgpu->drv->name,
+		   unique_id);
+	logline[0] = '\0';
+	cgpu->drv->get_statline_before(logline, sizeof(logline), cgpu);
+	devstatlen = strlen(logline);
+	if (devstatlen < STATBEFORELEN)
+		strncat(logline, blanks, STATBEFORELEN - devstatlen);
+	cg_wprintw(statuswin, "%s | ", logline);
+
+
+#ifdef USE_USBUTILS
+	if (cgpu->usbinfo.nodev)
+		cg_wprintw(statuswin, "ZOMBIE");
+	else
+#endif
+	if (cgpu->status == LIFE_DEAD)
+		cg_wprintw(statuswin, "DEAD  ");
+	else if (cgpu->status == LIFE_SICK)
+		cg_wprintw(statuswin, "SICK  ");
+	else if (cgpu->deven == DEV_DISABLED)
+		cg_wprintw(statuswin, "OFF   ");
+	else if (cgpu->deven == DEV_RECOVER)
+		cg_wprintw(statuswin, "REST  ");
+	else if (opt_widescreen) {
+		char displayed_hashes[16], displayed_rolling[16];
+		uint64_t d64;
+
+		d64 = (double)cgpu->total_mhashes / dev_runtime * 1000000ull;
+		suffix_string(d64, displayed_hashes, sizeof(displayed_hashes), 4);
+		d64 = (double)cgpu->rolling * 1000000ull;
+		suffix_string(d64, displayed_rolling, sizeof(displayed_rolling), 4);
+		adj_width(wu, &wuwidth);
+		adj_fwidth(cgpu->diff_accepted, &dawidth);
+		adj_fwidth(cgpu->diff_rejected, &drwidth);
+		adj_width(cgpu->hw_errors, &hwwidth);
+		cg_wprintw(statuswin, "%6s / %6sh/s WU:%*.1f/m "
+				"A:%*.0f R:%*.0f HW:%*d",
+				displayed_rolling,
+				displayed_hashes, wuwidth + 2, wu,
+				dawidth, cgpu->diff_accepted,
+				drwidth, cgpu->diff_rejected,
+				hwwidth, cgpu->hw_errors);
+	} else if (!alt_status) {
+		char displayed_hashes[16], displayed_rolling[16];
+		uint64_t d64;
+
+		d64 = (double)cgpu->total_mhashes / dev_runtime * 1000000ull;
+		suffix_string(d64, displayed_hashes, sizeof(displayed_hashes), 4);
+		d64 = (double)cgpu->rolling * 1000000ull;
+		suffix_string(d64, displayed_rolling, sizeof(displayed_rolling), 4);
+		adj_width(wu, &wuwidth);
+		cg_wprintw(statuswin, "%6s / %6sh/s WU:%*.1f/m", displayed_rolling,
+			   displayed_hashes, wuwidth + 2, wu);
+	} else {
+		adj_fwidth(cgpu->diff_accepted, &dawidth);
+		adj_fwidth(cgpu->diff_rejected, &drwidth);
+		adj_width(cgpu->hw_errors, &hwwidth);
+		cg_wprintw(statuswin, "A:%*.0f R:%*.0f HW:%*d",
+				dawidth, cgpu->diff_accepted,
+				drwidth, cgpu->diff_rejected,
+				hwwidth, cgpu->hw_errors);
+	}
+
+	logline[0] = '\0';
+	cgpu->drv->get_statline(logline, sizeof(logline), cgpu);
+	cg_wprintw(statuswin, "%s", logline);
+
+	wclrtoeol(statuswin);
+}
+#endif
+
+#ifdef HAVE_CURSES
+/* Check for window resize. Called with curses mutex locked */
+static inline void change_logwinsize(void)
+{
+	int x, y, logx, logy;
+
+	getmaxyx(mainwin, y, x);
+	if (x < 80 || y < 25)
+		return;
+
+	if (y > statusy + 2 && statusy < logstart) {
+		if (y - 2 < logstart)
+			statusy = y - 2;
+		else
+			statusy = logstart;
+		logcursor = statusy + 1;
+		mvwin(logwin, logcursor, 0);
+		wresize(statuswin, statusy, x);
+	}
+
+	y -= logcursor;
+	getmaxyx(logwin, logy, logx);
+	/* Detect screen size change */
+	if (x != logx || y != logy)
+		wresize(logwin, y, x);
+}
+
+static void check_winsizes(void)
+{
+	if (!use_curses)
+		return;
+	if (curses_active_locked()) {
+		int y, x;
+
+		erase();
+		x = getmaxx(statuswin);
+		if (logstart > LINES - 2)
+			statusy = LINES - 2;
+		else
+			statusy = logstart;
+		logcursor = statusy;
+		wresize(statuswin, statusy, x);
+		getmaxyx(mainwin, y, x);
+		y -= logcursor;
+		wresize(logwin, y, x);
+		mvwin(logwin, logcursor, 0);
+		unlock_curses();
+	}
+}
+
+static void disable_curses_windows(void);
+static void enable_curses_windows(void);
+
+static void switch_logsize(bool __maybe_unused newdevs)
+{
+	if (curses_active_locked()) {
+#ifdef WIN32
+		if (newdevs)
+			disable_curses_windows();
+#endif
+		if (opt_compact) {
+			logstart = devcursor + 1;
+			logcursor = logstart + 1;
+		} else {
+			logstart = devcursor + most_devices + 1;
+			logcursor = logstart + 1;
+		}
+#ifdef WIN32
+		if (newdevs)
+			enable_curses_windows();
+#endif
+		unlock_curses();
+		check_winsizes();
+	}
+}
+
+/* For mandatory printing when mutex is already locked */
+void _wlog(const char *str)
+{
+	wprintw(logwin, "%s", str);
+}
+
+/* Mandatory printing */
+void _wlogprint(const char *str)
+{
+	if (curses_active_locked()) {
+		wprintw(logwin, "%s", str);
+		unlock_curses();
+	}
+}
+#endif
+
+#ifdef HAVE_CURSES
+bool log_curses_only(int prio, const char *datetime, const char *str)
+{
+	bool high_prio;
+
+	high_prio = (prio == LOG_WARNING || prio == LOG_ERR);
+
+	if (curses_active_locked()) {
+		if (!opt_loginput || high_prio) {
+			wprintw(logwin, "%s%s\n", datetime, str);
+			if (high_prio) {
+				touchwin(logwin);
+				wrefresh(logwin);
+			}
+		}
+		unlock_curses();
+		return true;
+	}
+	return false;
+}
+
+void clear_logwin(void)
+{
+	if (curses_active_locked()) {
+		erase();
+		wclear(logwin);
+		unlock_curses();
+	}
+}
+
+void logwin_update(void)
+{
+	if (curses_active_locked()) {
+		touchwin(logwin);
+		wrefresh(logwin);
+		unlock_curses();
+	}
+}
+#endif
+
+static void enable_pool(struct pool *pool)
+{
+	if (pool->enabled != POOL_ENABLED) {
+		enabled_pools++;
+		pool->enabled = POOL_ENABLED;
+	}
+}
+
+#ifdef HAVE_CURSES
+static void disable_pool(struct pool *pool)
+{
+	if (pool->enabled == POOL_ENABLED)
+		enabled_pools--;
+	pool->enabled = POOL_DISABLED;
+}
+#endif
+
+static void reject_pool(struct pool *pool)
+{
+	if (pool->enabled == POOL_ENABLED)
+		enabled_pools--;
+	pool->enabled = POOL_REJECTING;
+}
+
+static void restart_threads(void);
+
+/* Theoretically threads could race when modifying accepted and
+ * rejected values but the chance of two submits completing at the
+ * same time is zero so there is no point adding extra locking */
+static void
+share_result(json_t *val, json_t *res, json_t *err, const struct work *work,
+	     char *hashshow, bool resubmit, char *worktime)
+{
+	struct pool *pool = work->pool;
+	struct cgpu_info *cgpu;
+
+	cgpu = get_thr_cgpu(work->thr_id);
+
+	if (json_is_true(res) || (work->gbt && json_is_null(res))) {
+		mutex_lock(&stats_lock);
+		cgpu->accepted++;
+		total_accepted++;
+		pool->accepted++;
+		cgpu->diff_accepted += work->work_difficulty;
+		total_diff_accepted += work->work_difficulty;
+		pool->diff_accepted += work->work_difficulty;
+		mutex_unlock(&stats_lock);
+
+		pool->seq_rejects = 0;
+		cgpu->last_share_pool = pool->pool_no;
+		cgpu->last_share_pool_time = time(NULL);
+		cgpu->last_share_diff = work->work_difficulty;
+		pool->last_share_time = cgpu->last_share_pool_time;
+		pool->last_share_diff = work->work_difficulty;
+		applog(LOG_DEBUG, "PROOF OF WORK RESULT: true (yay!!!)");
+		if (!QUIET) {
+			if (total_pools > 1)
+				applog(LOG_NOTICE, "Accepted %s %s %d pool %d %s%s",
+				       hashshow, cgpu->drv->name, cgpu->device_id, work->pool->pool_no, resubmit ? "(resubmit)" : "", worktime);
+			else
+				applog(LOG_NOTICE, "Accepted %s %s %d %s%s",
+				       hashshow, cgpu->drv->name, cgpu->device_id, resubmit ? "(resubmit)" : "", worktime);
+		}
+		sharelog("accept", work);
+		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;
+		}
+
+		/* Detect if a pool that has been temporarily disabled for
+		 * continually rejecting shares has started accepting shares.
+		 * This will only happen with the work returned from a
+		 * longpoll */
+		if (unlikely(pool->enabled == POOL_REJECTING)) {
+			applog(LOG_WARNING, "Rejecting pool %d now accepting shares, re-enabling!", pool->pool_no);
+			enable_pool(pool);
+			switch_pools(NULL);
+		}
+		/* If we know we found the block we know better than anyone
+		 * that new work is needed. */
+		if (unlikely(work->block))
+			restart_threads();
+	} else {
+		mutex_lock(&stats_lock);
+		cgpu->rejected++;
+		total_rejected++;
+		pool->rejected++;
+		cgpu->diff_rejected += work->work_difficulty;
+		total_diff_rejected += work->work_difficulty;
+		pool->diff_rejected += work->work_difficulty;
+		pool->seq_rejects++;
+		mutex_unlock(&stats_lock);
+
+		applog(LOG_DEBUG, "PROOF OF WORK RESULT: false (booooo)");
+		if (!QUIET) {
+			char where[20];
+			char disposition[36] = "reject";
+			char reason[32];
+
+			strcpy(reason, "");
+			if (total_pools > 1)
+				snprintf(where, sizeof(where), "pool %d", work->pool->pool_no);
+			else
+				strcpy(where, "");
+
+			if (!work->gbt)
+				res = json_object_get(val, "reject-reason");
+			if (res) {
+				const char *reasontmp = json_string_value(res);
+
+				size_t reasonLen = strlen(reasontmp);
+				if (reasonLen > 28)
+					reasonLen = 28;
+				reason[0] = ' '; reason[1] = '(';
+				cg_memcpy(2 + reason, reasontmp, reasonLen);
+				reason[reasonLen + 2] = ')'; reason[reasonLen + 3] = '\0';
+				cg_memcpy(disposition + 7, reasontmp, reasonLen);
+				disposition[6] = ':'; disposition[reasonLen + 7] = '\0';
+			} else if (work->stratum && err) {
+				if (json_is_array(err)) {
+					json_t *reason_val = json_array_get(err, 1);
+					char *reason_str;
+
+					if (reason_val && json_is_string(reason_val)) {
+						reason_str = (char *)json_string_value(reason_val);
+						snprintf(reason, 31, " (%s)", reason_str);
+					}
+				} else if (json_is_string(err)) {
+					const char *s = json_string_value(err);
+					snprintf(reason, 31, " (%s)", s);
+				}
+			}
+
+			applog(LOG_NOTICE, "Rejected %s %s %d %s%s %s%s",
+			       hashshow, cgpu->drv->name, cgpu->device_id, where, reason, resubmit ? "(resubmit)" : "", worktime);
+			sharelog(disposition, work);
+		}
+
+		/* Once we have more than a nominal amount of sequential rejects,
+		 * at least 10 and more than 3 mins at the current utility,
+		 * disable the pool because some pool error is likely to have
+		 * ensued. Do not do this if we know the share just happened to
+		 * be stale due to networking delays.
+		 */
+		if (pool->seq_rejects > 10 && !work->stale && opt_disable_pool && enabled_pools > 1) {
+			double utility = total_accepted / total_secs * 60;
+
+			if (pool->seq_rejects > utility * 3 && enabled_pools > 1) {
+				applog(LOG_WARNING, "Pool %d rejected %d sequential shares, disabling!",
+				       pool->pool_no, pool->seq_rejects);
+				reject_pool(pool);
+				if (pool == current_pool())
+					switch_pools(NULL);
+				pool->seq_rejects = 0;
+			}
+		}
+	}
+}
+
+static void show_hash(struct work *work, char *hashshow)
+{
+	unsigned char rhash[32];
+	char diffdisp[16];
+	unsigned long h32;
+	uint32_t *hash32;
+	uint64_t uintdiff;
+	int ofs;
+
+	swab256(rhash, work->hash);
+	for (ofs = 0; ofs <= 28; ofs ++) {
+		if (rhash[ofs])
+			break;
+	}
+	hash32 = (uint32_t *)(rhash + ofs);
+	h32 = be32toh(*hash32);
+	uintdiff = round(work->work_difficulty);
+	suffix_string(work->share_diff, diffdisp, sizeof (diffdisp), 0);
+	snprintf(hashshow, 64, "%08lx Diff %s/%"PRIu64"%s", h32, diffdisp, uintdiff,
+		 work->block? " BLOCK!" : "");
+}
+
+#ifdef HAVE_LIBCURL
+static void text_print_status(int thr_id)
+{
+	struct cgpu_info *cgpu;
+	char logline[256];
+
+	cgpu = get_thr_cgpu(thr_id);
+	if (cgpu) {
+		get_statline(logline, sizeof(logline), cgpu);
+		printf("%s\n", logline);
+	}
+}
+
+static void print_status(int thr_id)
+{
+	if (!curses_active)
+		text_print_status(thr_id);
+}
+
+static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit)
+{
+	json_t *val, *res, *err;
+	char *s;
+	bool rc = false;
+	int thr_id = work->thr_id;
+	struct cgpu_info *cgpu = get_thr_cgpu(thr_id);
+	struct pool *pool = work->pool;
+	int rolltime;
+	struct timeval tv_submit, tv_submit_reply;
+	char hashshow[64 + 4] = "";
+	char worktime[200] = "";
+	struct timeval now;
+	double dev_runtime;
+	char gbt_block[1024], varint[12];
+	unsigned char data[80];
+
+	/* build JSON-RPC request */
+	flip80(data, work->data);
+	__bin2hex(gbt_block, data, 80); // 160 length
+
+	if (work->gbt_txns < 0xfd) {
+		uint8_t val8 = work->gbt_txns;
+
+		__bin2hex(varint, (const unsigned char *)&val8, 1);
+	} else if (work->gbt_txns <= 0xffff) {
+		uint16_t val16 = htole16(work->gbt_txns);
+
+		strcat(gbt_block, "fd"); // +2
+		__bin2hex(varint, (const unsigned char *)&val16, 2);
+	} else {
+		uint32_t val32 = htole32(work->gbt_txns);
+
+		strcat(gbt_block, "fe"); // +2
+		__bin2hex(varint, (const unsigned char *)&val32, 4);
+	}
+	strcat(gbt_block, varint); // +8 max
+	strcat(gbt_block, work->coinbase);
+
+	s = cgmalloc(1024);
+	sprintf(s, "{\"id\": 0, \"method\": \"submitblock\", \"params\": [\"%s", gbt_block);
+	/* Has submit/coinbase support */
+	if (!pool->has_gbt) {
+		cg_rlock(&pool->gbt_lock);
+		if (pool->txn_data)
+			s = realloc_strcat(s, pool->txn_data);
+		cg_runlock(&pool->gbt_lock);
+	}
+	if (work->job_id) {
+		s = realloc_strcat(s, "\", {\"workid\": \"");
+		s = realloc_strcat(s, work->job_id);
+		s = realloc_strcat(s, "\"}]}");
+	} else
+		s = realloc_strcat(s, "\"]}");
+	applog(LOG_DEBUG, "DBG: sending %s submit RPC call: %s", pool->rpc_url, s);
+	s = realloc_strcat(s, "\n");
+
+	cgtime(&tv_submit);
+	/* issue JSON-RPC request */
+	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, false, false, &rolltime, pool, true);
+	cgtime(&tv_submit_reply);
+	free(s);
+
+	if (unlikely(!val)) {
+		applog(LOG_INFO, "submit_upstream_work json_rpc_call failed");
+		if (!pool_tset(pool, &pool->submit_fail)) {
+			total_ro++;
+			pool->remotefail_occasions++;
+			if (opt_lowmem) {
+				applog(LOG_WARNING, "Pool %d communication failure, discarding shares", pool->pool_no);
+				goto out;
+			}
+			applog(LOG_WARNING, "Pool %d communication failure, caching submissions", pool->pool_no);
+		}
+		cgsleep_ms(5000);
+		goto out;
+	} else if (pool_tclear(pool, &pool->submit_fail))
+		applog(LOG_WARNING, "Pool %d communication resumed, submitting work", pool->pool_no);
+
+	res = json_object_get(val, "result");
+	err = json_object_get(val, "error");
+
+	if (!QUIET) {
+		show_hash(work, hashshow);
+
+		if (opt_worktime) {
+			char workclone[20];
+			struct tm *tm, tm_getwork, tm_submit_reply;
+			double getwork_time = tdiff((struct timeval *)&(work->tv_getwork_reply),
+							(struct timeval *)&(work->tv_getwork));
+			double getwork_to_work = tdiff((struct timeval *)&(work->tv_work_start),
+							(struct timeval *)&(work->tv_getwork_reply));
+			double work_time = tdiff((struct timeval *)&(work->tv_work_found),
+							(struct timeval *)&(work->tv_work_start));
+			double work_to_submit = tdiff(&tv_submit,
+							(struct timeval *)&(work->tv_work_found));
+			double submit_time = tdiff(&tv_submit_reply, &tv_submit);
+			int diffplaces = 3;
+
+			time_t tmp_time = work->tv_getwork.tv_sec;
+			tm = localtime(&tmp_time);
+			cg_memcpy(&tm_getwork, tm, sizeof(struct tm));
+			tmp_time = tv_submit_reply.tv_sec;
+			tm = localtime(&tmp_time);
+			cg_memcpy(&tm_submit_reply, tm, sizeof(struct tm));
+
+			if (work->clone) {
+				snprintf(workclone, sizeof(workclone), "C:%1.3f",
+						tdiff((struct timeval *)&(work->tv_cloned),
+						(struct timeval *)&(work->tv_getwork_reply)));
+			}
+			else
+				strcpy(workclone, "O");
+
+			if (work->work_difficulty < 1)
+				diffplaces = 6;
+
+			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)be32toh(*(uint32_t *)&(work->data[28])),
+				(unsigned long)be32toh(*(uint32_t *)&(work->data[24])),
+				work->getwork_mode, diffplaces, work->work_difficulty,
+				tm_getwork.tm_hour, tm_getwork.tm_min,
+				tm_getwork.tm_sec, getwork_time, workclone,
+				getwork_to_work, work_time, work_to_submit, submit_time,
+				tm_submit_reply.tm_hour, tm_submit_reply.tm_min,
+				tm_submit_reply.tm_sec);
+		}
+	}
+
+	share_result(val, res, err, work, hashshow, resubmit, worktime);
+
+	if (cgpu->dev_start_tv.tv_sec == 0)
+		dev_runtime = total_secs;
+	else {
+		cgtime(&now);
+		dev_runtime = tdiff(&now, &(cgpu->dev_start_tv));
+	}
+
+	if (dev_runtime < 1.0)
+		dev_runtime = 1.0;
+
+	cgpu->utility = cgpu->accepted / dev_runtime * 60;
+
+	if (!opt_realquiet)
+		print_status(thr_id);
+	if (!want_per_device_stats) {
+		char logline[256];
+
+		get_statline(logline, sizeof(logline), cgpu);
+		applog(LOG_INFO, "%s", logline);
+	}
+
+	json_decref(val);
+
+	rc = true;
+out:
+	return rc;
+}
+#endif /* HAVE_LIBCURL */
+
+/* Specifies whether we can use this pool for work or not. */
+static bool pool_unusable(struct pool *pool)
+{
+	if (pool->idle)
+		return true;
+	if (pool->enabled != POOL_ENABLED)
+		return true;
+	if (pool->has_stratum && (!pool->stratum_active || !pool->stratum_notify))
+		return true;
+	return false;
+}
+
+/* In balanced mode, the amount of diff1 solutions per pool is monitored as a
+ * rolling average per 10 minutes and if pools start getting more, it biases
+ * away from them to distribute work evenly. The share count is reset to the
+ * rolling average every 10 minutes to not send all work to one pool after it
+ * has been disabled/out for an extended period. */
+static struct pool *select_balanced(struct pool *cp)
+{
+	int i, lowest = cp->shares;
+	struct pool *ret = cp;
+
+	for (i = 0; i < total_pools; i++) {
+		struct pool *pool = pools[i];
+
+		if (pool_unusable(pool))
+			continue;
+		if (pool->shares < lowest) {
+			lowest = pool->shares;
+			ret = pool;
+		}
+	}
+
+	ret->shares++;
+	return ret;
+}
+
+static struct pool *priority_pool(int choice);
+
+/* Select any active pool in a rotating fashion when loadbalance is chosen if
+ * it has any quota left. */
+static inline struct pool *select_pool(void)
+{
+	static int rotating_pool = 0;
+	struct pool *pool, *cp;
+	bool avail = false;
+	int tested, i;
+
+	cp = current_pool();
+
+	if (pool_strategy == POOL_BALANCE) {
+		pool = select_balanced(cp);
+		goto out;
+	}
+
+	if (pool_strategy != POOL_LOADBALANCE) {
+		pool = cp;
+		goto out;
+	} else
+		pool = NULL;
+
+	for (i = 0; i < total_pools; i++) {
+		struct pool *tp = pools[i];
+
+		if (tp->quota_used < tp->quota_gcd) {
+			avail = true;
+			break;
+		}
+	}
+
+	/* There are no pools with quota, so reset them. */
+	if (!avail) {
+		for (i = 0; i < total_pools; i++)
+			pools[i]->quota_used = 0;
+		if (++rotating_pool >= total_pools)
+			rotating_pool = 0;
+	}
+
+	/* Try to find the first pool in the rotation that is usable */
+	tested = 0;
+	while (!pool && tested++ < total_pools) {
+		pool = pools[rotating_pool];
+		if (pool->quota_used++ < pool->quota_gcd) {
+			if (!pool_unusable(pool))
+				break;
+		}
+		pool = NULL;
+		if (++rotating_pool >= total_pools)
+			rotating_pool = 0;
+	}
+
+	/* If there are no alive pools with quota, choose according to
+	 * priority. */
+	if (!pool) {
+		for (i = 0; i < total_pools; i++) {
+			struct pool *tp = priority_pool(i);
+
+			if (!pool_unusable(tp)) {
+				pool = tp;
+				break;
+			}
+		}
+	}
+
+	/* If still nothing is usable, use the current pool */
+	if (!pool)
+		pool = cp;
+out:
+	applog(LOG_DEBUG, "Selecting pool %d for work", pool->pool_no);
+	return pool;
+}
+
+/* truediffone == 0x00000000FFFF0000000000000000000000000000000000000000000000000000
+ * Generate a 256 bit binary LE target by cutting up diff into 64 bit sized
+ * portions or vice versa. */
+static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0;
+static const double bits192 = 6277101735386680763835789423207666416102355444464034512896.0;
+static const double bits128 = 340282366920938463463374607431768211456.0;
+static const double bits64 = 18446744073709551616.0;
+
+/* Converts a little endian 256 bit value to a double */
+static double le256todouble(const void *target)
+{
+	uint64_t *data64;
+	double dcut64;
+
+	data64 = (uint64_t *)(target + 24);
+	dcut64 = le64toh(*data64) * bits192;
+
+	data64 = (uint64_t *)(target + 16);
+	dcut64 += le64toh(*data64) * bits128;
+
+	data64 = (uint64_t *)(target + 8);
+	dcut64 += le64toh(*data64) * bits64;
+
+	data64 = (uint64_t *)(target);
+	dcut64 += le64toh(*data64);
+
+	return dcut64;
+}
+
+static double diff_from_target(void *target)
+{
+	double d64, dcut64;
+
+	d64 = truediffone;
+	dcut64 = le256todouble(target);
+	if (unlikely(!dcut64))
+		dcut64 = 1;
+	return d64 / dcut64;
+}
+
+/*
+ * Calculate the work->work_difficulty based on the work->target
+ */
+static void calc_diff(struct work *work, double known)
+{
+	struct cgminer_pool_stats *pool_stats = &(work->pool->cgminer_pool_stats);
+	double difficulty;
+	uint64_t uintdiff;
+
+	if (known)
+		work->work_difficulty = known;
+	else
+		work->work_difficulty = diff_from_target(work->target);
+
+	difficulty = work->work_difficulty;
+
+	pool_stats->last_diff = difficulty;
+	uintdiff = round(difficulty);
+	suffix_string(uintdiff, work->pool->diff, sizeof(work->pool->diff), 0);
+
+	if (difficulty == pool_stats->min_diff)
+		pool_stats->min_diff_count++;
+	else if (difficulty < pool_stats->min_diff || pool_stats->min_diff == 0) {
+		pool_stats->min_diff = difficulty;
+		pool_stats->min_diff_count = 1;
+	}
+
+	if (difficulty == pool_stats->max_diff)
+		pool_stats->max_diff_count++;
+	else if (difficulty > pool_stats->max_diff) {
+		pool_stats->max_diff = difficulty;
+		pool_stats->max_diff_count = 1;
+	}
+}
+
+static unsigned char bench_hidiff_bins[16][160];
+static unsigned char bench_lodiff_bins[16][160];
+static unsigned char bench_target[32];
+
+/* Iterate over the lo and hi diff benchmark work items such that we find one
+ * diff 32+ share every 32 work items. */
+static void get_benchmark_work(struct work *work)
+{
+	work->work_difficulty = 32;
+	cg_memcpy(work->target, bench_target, 32);
+	work->drv_rolllimit = 0;
+	work->mandatory = true;
+	work->pool = pools[0];
+	cgtime(&work->tv_getwork);
+	copy_time(&work->tv_getwork_reply, &work->tv_getwork);
+	work->getwork_mode = GETWORK_MODE_BENCHMARK;
+}
+
+static void benchfile_dspwork(struct work *work, uint32_t nonce)
+{
+	char buf[1024];
+	uint32_t dn;
+	int i;
+
+	dn = 0;
+	for (i = 0; i < 4; i++) {
+		dn *= 0x100;
+		dn += nonce & 0xff;
+		nonce /= 0x100;
+	}
+
+	if ((sizeof(work->data) * 2 + 1) > sizeof(buf))
+		quithere(1, "BENCHFILE Invalid buf size");
+
+	__bin2hex(buf, work->data, sizeof(work->data));
+
+	applog(LOG_ERR, "BENCHFILE nonce %u=0x%08x for work=%s",
+			(unsigned int)dn, (unsigned int)dn, buf);
+
+}
+
+static bool benchfile_get_work(struct work *work)
+{
+	char buf[1024];
+	char item[1024];
+	bool got = false;
+
+	if (!benchfile_in) {
+		if (opt_benchfile)
+			benchfile_in = fopen(opt_benchfile, "r");
+		else
+			quit(1, "BENCHFILE Invalid benchfile NULL");
+
+		if (!benchfile_in)
+			quit(1, "BENCHFILE Failed to open benchfile '%s'", opt_benchfile);
+
+		benchfile_line = 0;
+
+		if (!fgets(buf, 1024, benchfile_in))
+			quit(1, "BENCHFILE Failed to read benchfile '%s'", opt_benchfile);
+
+		got = true;
+		benchfile_work = 0;
+	}
+
+	if (!got) {
+		if (!fgets(buf, 1024, benchfile_in)) {
+			if (benchfile_work == 0)
+				quit(1, "BENCHFILE No work in benchfile '%s'", opt_benchfile);
+			fclose(benchfile_in);
+			benchfile_in = NULL;
+			return benchfile_get_work(work);
+		}
+	}
+
+	do {
+		benchfile_line++;
+
+		// Empty lines and lines starting with '#' or '/' are ignored
+		if (*buf != '\0' && *buf != '#' && *buf != '/') {
+			char *commas[BENCHWORK_COUNT];
+			int i, j, len;
+			long nonce_time;
+
+			commas[0] = buf;
+			for (i = 1; i < BENCHWORK_COUNT; i++) {
+				commas[i] = strchr(commas[i-1], ',');
+				if (!commas[i]) {
+					quit(1, "BENCHFILE Invalid input file line %d"
+						" - field count is %d but should be %d",
+						benchfile_line, i, BENCHWORK_COUNT);
+				}
+				len = commas[i] - commas[i-1];
+				if (benchfile_data[i-1].length &&
+				    (len != benchfile_data[i-1].length)) {
+					quit(1, "BENCHFILE Invalid input file line %d "
+						"field %d (%s) length is %d but should be %d",
+						benchfile_line, i,
+						benchfile_data[i-1].name,
+						len, benchfile_data[i-1].length);
+				}
+
+				*(commas[i]++) = '\0';
+			}
+
+			// NonceTime may have LF's etc
+			len = strlen(commas[BENCHWORK_NONCETIME]);
+			if (len < benchfile_data[BENCHWORK_NONCETIME].length) {
+				quit(1, "BENCHFILE Invalid input file line %d field %d"
+					" (%s) length is %d but should be least %d",
+					benchfile_line, BENCHWORK_NONCETIME+1,
+					benchfile_data[BENCHWORK_NONCETIME].name, len,
+					benchfile_data[BENCHWORK_NONCETIME].length);
+			}
+
+			sprintf(item, "0000000%c", commas[BENCHWORK_VERSION][0]);
+
+			j = strlen(item);
+			for (i = benchfile_data[BENCHWORK_PREVHASH].length-8; i >= 0; i -= 8) {
+				sprintf(&(item[j]), "%.8s", &commas[BENCHWORK_PREVHASH][i]);
+				j += 8;
+			}
+
+			for (i = benchfile_data[BENCHWORK_MERKLEROOT].length-8; i >= 0; i -= 8) {
+				sprintf(&(item[j]), "%.8s", &commas[BENCHWORK_MERKLEROOT][i]);
+				j += 8;
+			}
+
+			nonce_time = atol(commas[BENCHWORK_NONCETIME]);
+
+			sprintf(&(item[j]), "%08lx", nonce_time);
+			j += 8;
+
+			strcpy(&(item[j]), commas[BENCHWORK_DIFFBITS]);
+			j += benchfile_data[BENCHWORK_DIFFBITS].length;
+
+			memset(work, 0, sizeof(*work));
+
+			hex2bin(work->data, item, j >> 1);
+
+			calc_midstate(work->pool, work);
+
+			benchfile_work++;
+
+			return true;
+		}
+	} while (fgets(buf, 1024, benchfile_in));
+
+	if (benchfile_work == 0)
+		quit(1, "BENCHFILE No work in benchfile '%s'", opt_benchfile);
+	fclose(benchfile_in);
+	benchfile_in = NULL;
+	return benchfile_get_work(work);
+}
+
+static void get_benchfile_work(struct work *work)
+{
+	benchfile_get_work(work);
+	work->mandatory = true;
+	work->pool = pools[0];
+	cgtime(&work->tv_getwork);
+	copy_time(&work->tv_getwork_reply, &work->tv_getwork);
+	work->getwork_mode = GETWORK_MODE_BENCHMARK;
+	calc_diff(work, 0);
+}
+
+#ifdef HAVE_CURSES
+static void disable_curses_windows(void)
+{
+	leaveok(logwin, false);
+	leaveok(statuswin, false);
+	leaveok(mainwin, false);
+	nocbreak();
+	echo();
+	delwin(logwin);
+	delwin(statuswin);
+}
+
+/* Force locking of curses console_lock on shutdown since a dead thread might
+ * have grabbed the lock. */
+static bool curses_active_forcelocked(void)
+{
+	bool ret;
+
+	mutex_trylock(&console_lock);
+	ret = curses_active;
+	if (!ret)
+		unlock_curses();
+	return ret;
+}
+
+static void disable_curses(void)
+{
+	if (curses_active_forcelocked()) {
+		use_curses = false;
+		curses_active = false;
+		disable_curses_windows();
+		delwin(mainwin);
+		endwin();
+#ifdef WIN32
+		// Move the cursor to after curses output.
+		HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
+		CONSOLE_SCREEN_BUFFER_INFO csbi;
+		COORD coord;
+
+		if (GetConsoleScreenBufferInfo(hout, &csbi)) {
+			coord.X = 0;
+			coord.Y = csbi.dwSize.Y - 1;
+			SetConsoleCursorPosition(hout, coord);
+		}
+#endif
+		unlock_curses();
+	}
+}
+#endif
+
+static void kill_timeout(struct thr_info *thr)
+{
+	cg_completion_timeout(&thr_info_cancel, thr, 1000);
+}
+
+static void kill_mining(void)
+{
+	struct thr_info *thr;
+	int i;
+
+	forcelog(LOG_DEBUG, "Killing off mining threads");
+	/* Kill the mining threads*/
+	for (i = 0; i < mining_threads; i++) {
+		pthread_t *pth = NULL;
+
+		thr = get_thread(i);
+		if (thr && PTH(thr) != 0L)
+			pth = &thr->pth;
+		thr_info_cancel(thr);
+#if !defined __MINGW32__ || __WINPTHREADS_VERSION >= 0x00050000
+		if (pth && *pth)
+			pthread_join(*pth, NULL);
+#else
+		if (pth && pth->p)
+			pthread_join(*pth, NULL);
+#endif
+	}
+}
+
+static void wait_mining(void)
+{
+	struct thr_info *thr;
+	int i;
+
+	forcelog(LOG_DEBUG, "Waiting on mining threads");
+	/* Kill the mining threads*/
+	for (i = 0; i < mining_threads; i++) {
+		pthread_t *pth = NULL;
+
+		thr = get_thread(i);
+		if (thr && PTH(thr) != 0L)
+			pth = &thr->pth;
+		if (pth && *pth)
+			pthread_join(*pth, NULL);
+	}
+}
+
+static void __kill_work(void)
+{
+	struct thr_info *thr;
+	int i;
+
+	if (!successful_connect)
+		return;
+
+	forcelog(LOG_INFO, "Received kill message");
+
+#ifdef USE_USBUTILS
+	/* Best to get rid of it first so it doesn't
+	 * try to create any new devices */
+	forcelog(LOG_DEBUG, "Killing off HotPlug thread");
+	thr = &control_thr[hotplug_thr_id];
+	kill_timeout(thr);
+#endif
+
+	forcelog(LOG_DEBUG, "Killing off watchpool thread");
+	/* Kill the watchpool thread */
+	thr = &control_thr[watchpool_thr_id];
+	kill_timeout(thr);
+
+	forcelog(LOG_DEBUG, "Killing off watchdog thread");
+	/* Kill the watchdog thread */
+	thr = &control_thr[watchdog_thr_id];
+	kill_timeout(thr);
+
+	forcelog(LOG_DEBUG, "Shutting down mining threads");
+	for (i = 0; i < mining_threads; i++) {
+		struct cgpu_info *cgpu;
+
+		thr = get_thread(i);
+		if (!thr)
+			continue;
+		cgpu = thr->cgpu;
+		if (!cgpu)
+			continue;
+
+		cgpu->shutdown = true;
+	}
+
+	/* Give the threads a chance to shut down gracefully */
+	cg_completion_timeout(&wait_mining, NULL, 5000);
+	/* Kill the threads and wait for them to return if not */
+	cg_completion_timeout(&kill_mining, NULL, 5000);
+
+	/* Stop the others */
+	forcelog(LOG_DEBUG, "Killing off API thread");
+	thr = &control_thr[api_thr_id];
+	kill_timeout(thr);
+
+#ifdef USE_USBUTILS
+	/* Release USB resources in case it's a restart
+	 * and not a QUIT */
+	forcelog(LOG_DEBUG, "Releasing all USB devices");
+	cg_completion_timeout(&usb_cleanup, NULL, 1000);
+
+	forcelog(LOG_DEBUG, "Killing off usbres thread");
+	thr = &control_thr[usbres_thr_id];
+	kill_timeout(thr);
+#endif
+
+}
+
+/* This should be the common exit path */
+void kill_work(void)
+{
+	cg_completion_timeout(&__kill_work, NULL, 10000);
+
+	quit(0, "Shutdown signal received.");
+}
+
+static
+#ifdef WIN32
+const
+#endif
+char **initial_args;
+
+static void *raise_thread(void __maybe_unused *arg)
+{
+	raise(SIGTERM);
+	return NULL;
+}
+
+/* This provides a mechanism for driver threads to initiate a shutdown without
+ * the cyclical problem of the shutdown path being cancelled while the driver
+ * thread shuts down.*/
+void raise_cgminer(void)
+{
+	pthread_t pth;
+
+	pthread_create(&pth, NULL, raise_thread, NULL);
+}
+
+static void clean_up(bool restarting);
+
+void app_restart(void)
+{
+	applog(LOG_WARNING, "Attempting to restart %s", packagename);
+#ifdef USE_LIBSYSTEMD
+	sd_notify(false, "RELOADING=1\n"
+		"STATUS=Restarting...");
+#endif
+
+	cg_completion_timeout(&__kill_work, NULL, 5000);
+	clean_up(true);
+
+#if defined(unix) || defined(__APPLE__)
+	if (forkpid > 0) {
+		kill(forkpid, SIGTERM);
+		forkpid = 0;
+	}
+#endif
+
+	execv(initial_args[0], (EXECV_2ND_ARG_TYPE)initial_args);
+	applog(LOG_WARNING, "Failed to restart application");
+}
+
+static void sighandler(int __maybe_unused sig)
+{
+	/* Restore signal handlers so we can still quit if kill_work fails */
+	sigaction(SIGTERM, &termhandler, NULL);
+	sigaction(SIGINT, &inthandler, NULL);
+	sigaction(SIGABRT, &abrthandler, NULL);
+	kill_work();
+}
+
+static void _stage_work(struct work *work);
+
+#define stage_work(WORK) do { \
+	_stage_work(WORK); \
+	WORK = NULL; \
+} while (0)
+
+/* Adjust an existing char ntime field with a relative noffset */
+static void modify_ntime(char *ntime, int noffset)
+{
+	unsigned char bin[4];
+	uint32_t h32, *be32 = (uint32_t *)bin;
+
+	hex2bin(bin, ntime, 4);
+	h32 = be32toh(*be32) + noffset;
+	*be32 = htobe32(h32);
+	__bin2hex(ntime, bin, 4);
+}
+
+void roll_work(struct work *work)
+{
+	uint32_t *work_ntime;
+	uint32_t ntime;
+
+	work_ntime = (uint32_t *)(work->data + 68);
+	ntime = be32toh(*work_ntime);
+	ntime++;
+	*work_ntime = htobe32(ntime);
+	local_work++;
+	work->rolls++;
+	work->nonce = 0;
+	applog(LOG_DEBUG, "Successfully rolled work");
+	/* Change the ntime field if this is stratum work */
+	if (work->ntime)
+		modify_ntime(work->ntime, 1);
+
+	/* This is now a different work item so it needs a different ID for the
+	 * hashtable */
+	work->id = total_work_inc();
+}
+
+void roll_work_ntime(struct work *work, int noffset)
+{
+	uint32_t *work_ntime;
+	uint32_t ntime;
+
+	work_ntime = (uint32_t *)(work->data + 68);
+	ntime = be32toh(*work_ntime);
+	ntime += noffset;
+	*work_ntime = htobe32(ntime);
+	local_work++;
+	work->rolls += noffset;
+	work->nonce = 0;
+	applog(LOG_DEBUG, "Successfully rolled work");
+
+	/* Change the ntime field if this is stratum work */
+	if (work->ntime)
+		modify_ntime(work->ntime, noffset);
+
+	/* This is now a different work item so it needs a different ID for the
+	 * hashtable */
+	work->id = total_work_inc();
+}
+
+struct work *make_clone(struct work *work)
+{
+	struct work *work_clone = copy_work(work);
+
+	work_clone->clone = true;
+	cgtime((struct timeval *)&(work_clone->tv_cloned));
+	work_clone->longpoll = false;
+	work_clone->mandatory = false;
+	/* Make cloned work appear slightly older to bias towards keeping the
+	 * master work item which can be further rolled */
+	work_clone->tv_staged.tv_sec -= 1;
+
+	return work_clone;
+}
+
+#ifdef HAVE_LIBCURL
+/* Called with pool_lock held. Recruit an extra curl if none are available for
+ * this pool. */
+static void recruit_curl(struct pool *pool)
+{
+	struct curl_ent *ce = cgcalloc(sizeof(struct curl_ent), 1);
+
+	ce->curl = curl_easy_init();
+	if (unlikely(!ce->curl))
+		quit(1, "Failed to init in recruit_curl");
+
+	list_add(&ce->node, &pool->curlring);
+	pool->curls++;
+}
+
+/* Grab an available curl if there is one. If not, then recruit extra curls
+ * unless we are in a submit_fail situation, or we have opt_delaynet enabled
+ * and there are already 5 curls in circulation. Limit total number to the
+ * number of mining threads per pool as well to prevent blasting a pool during
+ * network delays/outages. */
+static struct curl_ent *pop_curl_entry(struct pool *pool)
+{
+	int curl_limit = opt_delaynet ? 5 : (mining_threads + max_queue) * 2;
+	bool recruited = false;
+	struct curl_ent *ce;
+
+	mutex_lock(&pool->pool_lock);
+retry:
+	if (!pool->curls) {
+		recruit_curl(pool);
+		recruited = true;
+	} else if (list_empty(&pool->curlring)) {
+		if (pool->curls >= curl_limit) {
+			pthread_cond_wait(&pool->cr_cond, &pool->pool_lock);
+			goto retry;
+		} else {
+			recruit_curl(pool);
+			recruited = true;
+		}
+	}
+	ce = list_entry(pool->curlring.next, struct curl_ent, node);
+	list_del(&ce->node);
+	mutex_unlock(&pool->pool_lock);
+
+	if (recruited)
+		applog(LOG_DEBUG, "Recruited curl for pool %d", pool->pool_no);
+	return ce;
+}
+
+static void push_curl_entry(struct curl_ent *ce, struct pool *pool)
+{
+	mutex_lock(&pool->pool_lock);
+	list_add_tail(&ce->node, &pool->curlring);
+	cgtime(&ce->tv);
+	pthread_cond_broadcast(&pool->cr_cond);
+	mutex_unlock(&pool->pool_lock);
+}
+
+static bool stale_work(struct work *work, bool share);
+
+static inline bool should_roll(struct work *work)
+{
+	struct timeval now;
+	time_t expiry;
+
+	if (work->pool != current_pool() && pool_strategy != POOL_LOADBALANCE && pool_strategy != POOL_BALANCE)
+		return false;
+
+	if (work->rolltime > max_scantime)
+		expiry = work->rolltime;
+	else
+		expiry = max_scantime;
+	expiry = expiry * 2 / 3;
+
+	/* We shouldn't roll if we're unlikely to get one shares' duration
+	 * work out of doing so */
+	cgtime(&now);
+	if (now.tv_sec - work->tv_staged.tv_sec > expiry)
+		return false;
+
+	return true;
+}
+
+/* Limit rolls to 7000 to not beyond 2 hours in the future where bitcoind will
+ * reject blocks as invalid. */
+static inline bool can_roll(struct work *work)
+{
+	return (!work->stratum && work->pool && work->rolltime && !work->clone &&
+		work->rolls < 7000 && !stale_work(work, false));
+}
+
+static void *submit_work_thread(void *userdata)
+{
+	struct work *work = (struct work *)userdata;
+	struct pool *pool = work->pool;
+	bool resubmit = false;
+	struct curl_ent *ce;
+
+	pthread_detach(pthread_self());
+
+	RenameThread("SubmitWork");
+
+	applog(LOG_DEBUG, "Creating extra submit work thread");
+
+	ce = pop_curl_entry(pool);
+	/* submit solution to bitcoin via JSON-RPC */
+	while (!submit_upstream_work(work, ce->curl, resubmit)) {
+		if (opt_lowmem) {
+			applog(LOG_NOTICE, "Pool %d share being discarded to minimise memory cache", pool->pool_no);
+			break;
+		}
+		resubmit = true;
+		if (stale_work(work, true)) {
+			applog(LOG_NOTICE, "Pool %d share became stale while retrying submit, discarding", pool->pool_no);
+
+			mutex_lock(&stats_lock);
+			total_stale++;
+			pool->stale_shares++;
+			total_diff_stale += work->work_difficulty;
+			pool->diff_stale += work->work_difficulty;
+			mutex_unlock(&stats_lock);
+
+			free_work(work);
+			break;
+		}
+
+		/* pause, then restart work-request loop */
+		applog(LOG_INFO, "json_rpc_call failed on submit_work, retrying");
+	}
+	push_curl_entry(ce, pool);
+
+	return NULL;
+}
+
+/* Clones work by rolling it if possible, and returning a clone instead of the
+ * original work item which gets staged again to possibly be rolled again in
+ * the future */
+static struct work *clone_work(struct work *work)
+{
+	int mrs = mining_threads + max_queue - total_staged();
+	struct work *work_clone;
+	bool cloned;
+
+	if (mrs < 1)
+		return work;
+
+	cloned = false;
+	work_clone = make_clone(work);
+	while (mrs-- > 0 && can_roll(work) && should_roll(work)) {
+		applog(LOG_DEBUG, "Pushing rolled converted work to stage thread");
+		stage_work(work_clone);
+		roll_work(work);
+		work_clone = make_clone(work);
+		/* Roll it again to prevent duplicates should this be used
+		 * directly later on */
+		roll_work(work);
+		cloned = true;
+	}
+
+	if (cloned) {
+		stage_work(work);
+		return work_clone;
+	}
+
+	free_work(work_clone);
+
+	return work;
+}
+
+#else /* HAVE_LIBCURL */
+static void *submit_work_thread(void __maybe_unused *userdata)
+{
+	pthread_detach(pthread_self());
+	return NULL;
+}
+#endif /* HAVE_LIBCURL */
+
+/* Return an adjusted ntime if we're submitting work that a device has
+ * internally offset the ntime. */
+static char *offset_ntime(const char *ntime, int noffset)
+{
+	unsigned char bin[4];
+	uint32_t h32, *be32 = (uint32_t *)bin;
+
+	hex2bin(bin, ntime, 4);
+	h32 = be32toh(*be32) + noffset;
+	*be32 = htobe32(h32);
+
+	return bin2hex(bin, 4);
+}
+
+/* Duplicates any dynamically allocated arrays within the work struct to
+ * prevent a copied work struct from freeing ram belonging to another struct */
+static void _copy_work(struct work *work, const struct work *base_work, int noffset)
+{
+	uint32_t id = work->id;
+
+	clean_work(work);
+	cg_memcpy(work, base_work, sizeof(struct work));
+	/* Keep the unique new id assigned during make_work to prevent copied
+	 * work from having the same id. */
+	work->id = id;
+	if (base_work->job_id)
+		work->job_id = strdup(base_work->job_id);
+	if (base_work->nonce1)
+		work->nonce1 = strdup(base_work->nonce1);
+	if (base_work->ntime) {
+		/* If we are passed an noffset the binary work->data ntime and
+		 * the work->ntime hex string need to be adjusted. */
+		if (noffset) {
+			uint32_t *work_ntime = (uint32_t *)(work->data + 68);
+			uint32_t ntime = be32toh(*work_ntime);
+
+			ntime += noffset;
+			*work_ntime = htobe32(ntime);
+			work->ntime = offset_ntime(base_work->ntime, noffset);
+		} else
+			work->ntime = strdup(base_work->ntime);
+	} else if (noffset) {
+		uint32_t *work_ntime = (uint32_t *)(work->data + 68);
+		uint32_t ntime = be32toh(*work_ntime);
+
+		ntime += noffset;
+		*work_ntime = htobe32(ntime);
+	}
+	if (base_work->coinbase)
+		work->coinbase = strdup(base_work->coinbase);
+#ifdef USE_BITMAIN_SOC
+	work->version = base_work->version;
+#endif
+}
+
+void set_work_ntime(struct work *work, int ntime)
+{
+	uint32_t *work_ntime = (uint32_t *)(work->data + 68);
+
+	*work_ntime = htobe32(ntime);
+	if (work->ntime) {
+		free(work->ntime);
+		work->ntime = bin2hex((unsigned char *)work_ntime, 4);
+	}
+}
+
+/* Generates a copy of an existing work struct, creating fresh heap allocations
+ * for all dynamically allocated arrays within the struct. noffset is used for
+ * when a driver has internally rolled the ntime, noffset is a relative value.
+ * The macro copy_work() calls this function with an noffset of 0. */
+struct work *copy_work_noffset(struct work *base_work, int noffset)
+{
+	struct work *work = make_work();
+
+	_copy_work(work, base_work, noffset);
+
+	return work;
+}
+
+void pool_died(struct pool *pool)
+{
+	if (!pool_tset(pool, &pool->idle)) {
+		cgtime(&pool->tv_idle);
+		if (pool == current_pool()) {
+			applog(LOG_WARNING, "Pool %d %s not responding!", pool->pool_no, pool->rpc_url);
+			switch_pools(NULL);
+		} else
+			applog(LOG_INFO, "Pool %d %s failed to return work", pool->pool_no, pool->rpc_url);
+	}
+}
+
+static bool stale_work(struct work *work, bool share)
+{
+	struct timeval now;
+	time_t work_expiry;
+	struct pool *pool;
+
+	if (opt_benchmark || opt_benchfile)
+		return false;
+
+	if (work->work_block != work_block) {
+		applog(LOG_DEBUG, "Work stale due to block mismatch");
+		return true;
+	}
+
+	/* Technically the rolltime should be correct but some pools
+	 * advertise a broken expire= that is lower than a meaningful
+	 * scantime */
+	if (work->rolltime > max_scantime)
+		work_expiry = work->rolltime;
+	else
+		work_expiry = max_expiry;
+
+	pool = work->pool;
+
+	if (!share && pool->has_stratum) {
+		bool same_job;
+
+		if (!pool->stratum_active || !pool->stratum_notify) {
+			applog(LOG_DEBUG, "Work stale due to stratum inactive");
+			return true;
+		}
+
+		same_job = true;
+
+		cg_rlock(&pool->data_lock);
+		if (strcmp(work->job_id, pool->swork.job_id))
+			same_job = false;
+		cg_runlock(&pool->data_lock);
+
+		if (!same_job) {
+			applog(LOG_DEBUG, "Work stale due to stratum job_id mismatch");
+			return true;
+		}
+	}
+
+	if (unlikely(work_expiry < 5))
+		work_expiry = 5;
+
+	cgtime(&now);
+	if ((now.tv_sec - work->tv_staged.tv_sec) >= work_expiry) {
+		applog(LOG_DEBUG, "Work stale due to expiry");
+		return true;
+	}
+
+	return false;
+}
+
+uint64_t share_diff(const struct work *work)
+{
+	bool new_best = false;
+	double d64, s64;
+	uint64_t ret;
+
+	d64 = truediffone;
+	s64 = le256todouble(work->hash);
+	if (unlikely(!s64))
+		s64 = 0;
+
+	ret = round(d64 / s64);
+
+	cg_wlock(&control_lock);
+	if (unlikely(ret > best_diff)) {
+		new_best = true;
+		best_diff = ret;
+		suffix_string(best_diff, best_share, sizeof(best_share), 0);
+	}
+	if (unlikely(ret > work->pool->best_diff))
+		work->pool->best_diff = ret;
+	cg_wunlock(&control_lock);
+
+	if (unlikely(new_best))
+		applog(LOG_INFO, "New best share: %s", best_share);
+
+	return ret;
+}
+
+static void regen_hash(struct work *work)
+{
+	uint32_t *data32 = (uint32_t *)(work->data);
+	unsigned char swap[80];
+	uint32_t *swap32 = (uint32_t *)swap;
+	unsigned char hash1[32];
+
+	flip80(swap32, data32);
+	sha256(swap, 80, hash1);
+	sha256(hash1, 32, (unsigned char *)(work->hash));
+}
+
+static bool cnx_needed(struct pool *pool);
+
+/* Find the pool that currently has the highest priority */
+static struct pool *priority_pool(int choice)
+{
+	struct pool *ret = NULL;
+	int i;
+
+	for (i = 0; i < total_pools; i++) {
+		struct pool *pool = pools[i];
+
+		if (pool->prio == choice) {
+			ret = pool;
+			break;
+		}
+	}
+
+	if (unlikely(!ret)) {
+		applog(LOG_ERR, "WTF No pool %d found!", choice);
+		return pools[choice];
+	}
+	return ret;
+}
+
+void switch_pools(struct pool *selected)
+{
+	struct pool *pool, *last_pool;
+	int i, pool_no, next_pool;
+
+	cg_wlock(&control_lock);
+	last_pool = currentpool;
+	pool_no = currentpool->pool_no;
+
+	/* Switch selected to pool number 0 and move the rest down */
+	if (selected) {
+		if (selected->prio != 0) {
+			for (i = 0; i < total_pools; i++) {
+				pool = pools[i];
+				if (pool->prio < selected->prio)
+					pool->prio++;
+			}
+			selected->prio = 0;
+		}
+	}
+
+	switch (pool_strategy) {
+		/* All of these set to the master pool */
+		case POOL_BALANCE:
+		case POOL_FAILOVER:
+		case POOL_LOADBALANCE:
+			for (i = 0; i < total_pools; i++) {
+				pool = priority_pool(i);
+				if (pool_unusable(pool))
+					continue;
+				pool_no = pool->pool_no;
+				break;
+			}
+			break;
+		/* Both of these simply increment and cycle */
+		case POOL_ROUNDROBIN:
+		case POOL_ROTATE:
+			if (selected && !selected->idle) {
+				pool_no = selected->pool_no;
+				break;
+			}
+			next_pool = pool_no;
+			/* Select the next alive pool */
+			for (i = 1; i < total_pools; i++) {
+				next_pool++;
+				if (next_pool >= total_pools)
+					next_pool = 0;
+				pool = pools[next_pool];
+				if (pool_unusable(pool))
+					continue;
+				pool_no = next_pool;
+				break;
+			}
+			break;
+		default:
+			break;
+	}
+
+	currentpool = pools[pool_no];
+	pool = currentpool;
+	cg_wunlock(&control_lock);
+
+	if (pool != last_pool && pool_strategy != POOL_LOADBALANCE && pool_strategy != POOL_BALANCE) {
+		applog(LOG_WARNING, "Switching to pool %d %s", pool->pool_no, pool->rpc_url);
+		clear_pool_work(last_pool);
+	}
+
+	mutex_lock(&lp_lock);
+	pthread_cond_broadcast(&lp_cond);
+	mutex_unlock(&lp_lock);
+}
+
+void _discard_work(struct work **workptr, const char *file, const char *func, const int line)
+{
+	struct work *work = *workptr;
+
+	if (unlikely(!work)) {
+		applog(LOG_ERR, "Discard work called with null work from %s %s:%d",
+		       file, func, line);
+		return;
+	}
+	if (!work->clone && !work->rolls && !work->mined) {
+		if (work->pool) {
+			work->pool->discarded_work++;
+			work->pool->quota_used--;
+			work->pool->works--;
+		}
+		total_discarded++;
+		applog(LOG_DEBUG, "Discarded work");
+	} else
+		applog(LOG_DEBUG, "Discarded cloned or rolled work");
+	_free_work(workptr, file, func, line);
+}
+
+static void wake_gws(void)
+{
+	mutex_lock(stgd_lock);
+	pthread_cond_signal(&gws_cond);
+	mutex_unlock(stgd_lock);
+}
+
+static void discard_stale(void)
+{
+	struct work *work, *tmp;
+	int stale = 0;
+
+	mutex_lock(stgd_lock);
+	HASH_ITER(hh, staged_work, work, tmp) {
+		if (stale_work(work, false)) {
+			HASH_DEL(staged_work, work);
+			discard_work(work);
+			stale++;
+		}
+	}
+	pthread_cond_signal(&gws_cond);
+	mutex_unlock(stgd_lock);
+
+	if (stale)
+		applog(LOG_DEBUG, "Discarded %d stales that didn't match current hash", stale);
+}
+
+/* A generic wait function for threads that poll that will wait a specified
+ * time tdiff waiting on the pthread conditional that is broadcast when a
+ * work restart is required. Returns the value of pthread_cond_timedwait
+ * which is zero if the condition was met or ETIMEDOUT if not.
+ */
+int restart_wait(struct thr_info *thr, unsigned int mstime)
+{
+	struct timespec abstime, tdiff;
+	int rc;
+
+	cgcond_time(&abstime);
+	ms_to_timespec(&tdiff, mstime);
+	timeraddspec(&abstime, &tdiff);
+
+	mutex_lock(&restart_lock);
+	if (thr->work_restart)
+		rc = 0;
+	else
+		rc = pthread_cond_timedwait(&restart_cond, &restart_lock, &abstime);
+	mutex_unlock(&restart_lock);
+
+	return rc;
+}
+
+static void *restart_thread(void __maybe_unused *arg)
+{
+	struct cgpu_info *cgpu;
+	int i, mt;
+
+	pthread_detach(pthread_self());
+
+	/* Discard staged work that is now stale */
+	discard_stale();
+
+	rd_lock(&mining_thr_lock);
+	mt = mining_threads;
+	rd_unlock(&mining_thr_lock);
+
+	for (i = 0; i < mt; i++) {
+		cgpu = mining_thr[i]->cgpu;
+		if (unlikely(!cgpu))
+			continue;
+		if (cgpu->deven != DEV_ENABLED)
+			continue;
+		mining_thr[i]->work_restart = true;
+		flush_queue(cgpu);
+		cgpu->drv->flush_work(cgpu);
+	}
+
+	mutex_lock(&restart_lock);
+	pthread_cond_broadcast(&restart_cond);
+	mutex_unlock(&restart_lock);
+
+#ifdef USE_USBUTILS
+	/* Cancels any cancellable usb transfers. Flagged as such it means they
+	 * are usualy waiting on a read result and it's safe to abort the read
+	 * early. */
+	cancel_usb_transfers();
+#endif
+	return NULL;
+}
+
+/* In order to prevent a deadlock via the various drv->flush_work
+ * implementations we send the restart messages via a separate thread. */
+static void restart_threads(void)
+{
+	pthread_t rthread;
+
+	cgtime(&restart_tv_start);
+	if (unlikely(pthread_create(&rthread, NULL, restart_thread, NULL)))
+		quithere(1, "Failed to create restart thread errno=%d", errno);
+}
+
+static void signal_work_update(void)
+{
+	int i;
+
+	applog(LOG_INFO, "Work update message received");
+
+	cgtime(&update_tv_start);
+	rd_lock(&mining_thr_lock);
+	for (i = 0; i < mining_threads; i++)
+		mining_thr[i]->work_update = true;
+	rd_unlock(&mining_thr_lock);
+}
+#if defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_AVALON9) || defined(USE_AVALONLC3)
+static void signal_clean_jobs(void)
+{
+	int i;
+
+	applog(LOG_NOTICE, "Job clean message received");
+
+	rd_lock(&mining_thr_lock);
+	for (i = 0; i < mining_threads; i++)
+		mining_thr[i]->clean_jobs = true;
+	rd_unlock(&mining_thr_lock);
+}
+#endif
+
+static void set_curblock(const char *hexstr, const unsigned char *bedata)
+{
+	int ofs;
+
+	cg_wlock(&ch_lock);
+	cgtime_real(&block_timeval);
+	strcpy(current_hash, hexstr);
+	cg_memcpy(current_block, bedata, 32);
+	get_timestamp(blocktime, sizeof(blocktime), &block_timeval);
+	cg_wunlock(&ch_lock);
+
+	for (ofs = 0; ofs <= 56; ofs++) {
+		if (memcmp(&current_hash[ofs], "0", 1))
+			break;
+	}
+	strncpy(prev_block, &current_hash[ofs], 8);
+	prev_block[8] = '\0';
+
+	applog(LOG_INFO, "New block: %s... diff %s", current_hash, block_diff);
+}
+
+static int block_sort(struct block *blocka, struct block *blockb)
+{
+	return blocka->block_no - blockb->block_no;
+}
+
+/* Decode the current block difficulty which is in packed form */
+static void set_blockdiff(const struct work *work)
+{
+	uint8_t pow = work->data[72];
+	int powdiff = (8 * (0x1d - 3)) - (8 * (pow - 3));
+#if defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_AVALON9) || defined(USE_AVALONLC3)
+	if (powdiff < 8)
+		powdiff = 8;
+#else
+	if (powdiff < 0)
+		powdiff = 0;
+#endif
+	uint32_t diff32 = be32toh(*((uint32_t *)(work->data + 72))) & 0x00FFFFFF;
+	double numerator = 0xFFFFULL << powdiff;
+	double ddiff = numerator / (double)diff32;
+
+	if (unlikely(current_diff != ddiff)) {
+		suffix_string(ddiff, block_diff, sizeof(block_diff), 0);
+		current_diff = ddiff;
+		applog(LOG_NOTICE, "Network diff set to %s", block_diff);
+	}
+}
+
+/* Search to see if this string is from a block that has been seen before */
+static bool block_exists(const char *hexstr, const unsigned char *bedata, const struct work *work)
+{
+	int deleted_block = 0;
+	struct block *s;
+	bool ret = true;
+
+	wr_lock(&blk_lock);
+	HASH_FIND_STR(blocks, hexstr, s);
+	if (!s) {
+		s = cgcalloc(sizeof(struct block), 1);
+		if (unlikely(!s))
+			quit (1, "block_exists OOM");
+		strcpy(s->hash, hexstr);
+		s->block_no = new_blocks++;
+
+		ret = false;
+		/* Only keep the last hour's worth of blocks in memory since
+		 * work from blocks before this is virtually impossible and we
+		 * want to prevent memory usage from continually rising */
+		if (HASH_COUNT(blocks) > 6) {
+			struct block *oldblock;
+
+			HASH_SORT(blocks, block_sort);
+			oldblock = blocks;
+			deleted_block = oldblock->block_no;
+			HASH_DEL(blocks, oldblock);
+			free(oldblock);
+		}
+		HASH_ADD_STR(blocks, hash, s);
+		set_blockdiff(work);
+		if (deleted_block)
+			applog(LOG_DEBUG, "Deleted block %d from database", deleted_block);
+	}
+	wr_unlock(&blk_lock);
+
+	if (!ret)
+		set_curblock(hexstr, bedata);
+	if (deleted_block)
+		applog(LOG_DEBUG, "Deleted block %d from database", deleted_block);
+
+	return ret;
+}
+
+static bool test_work_current(struct work *work)
+{
+	struct pool *pool = work->pool;
+	unsigned char bedata[32];
+	char hexstr[68];
+	bool ret = true;
+	unsigned char *bin_height = &pool->coinbase[43];
+	uint8_t cb_height_sz = bin_height[-1];
+	uint32_t height = 0;
+
+	if (work->mandatory)
+		return ret;
+
+	swap256(bedata, work->data + 4);
+	__bin2hex(hexstr, bedata, 32);
+
+	/* Calculate block height */
+	if (cb_height_sz <= 4) {
+		memcpy(&height, bin_height, cb_height_sz);
+		height = le32toh(height);
+		height--;
+	}
+
+	cg_wlock(&pool->data_lock);
+	if (pool->swork.clean) {
+		pool->swork.clean = false;
+		work->longpoll = true;
+#if defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_AVALON9) || defined(USE_AVALONLC3)
+		opt_clean_jobs = true;
+#endif
+	}
+	if (pool->current_height != height) {
+		pool->current_height = height;
+	}
+	cg_wunlock(&pool->data_lock);
+
+	/* Search to see if this block exists yet and if not, consider it a
+	 * new block and set the current block details to this one */
+	if (!block_exists(hexstr, bedata, work)) {
+		/* Copy the information to this pool's prev_block since it
+		 * knows the new block exists. */
+		cg_memcpy(pool->prev_block, bedata, 32);
+		if (unlikely(new_blocks == 1)) {
+			ret = false;
+			goto out;
+		}
+
+		work->work_block = ++work_block;
+
+		if (work->longpoll) {
+			if (work->stratum) {
+				applog(LOG_NOTICE, "Stratum from pool %d detected new block at height %d",
+				       pool->pool_no, height);
+			} else {
+				applog(LOG_NOTICE, "%sLONGPOLL from pool %d detected new block at height %d",
+				       work->gbt ? "GBT " : "", pool->pool_no, height);
+			}
+		} else if (have_longpoll && !pool->gbt_solo)
+			applog(LOG_NOTICE, "New block detected on network before pool notification from pool %d at height %d",
+			       pool->pool_no, height);
+		else if (!pool->gbt_solo)
+			applog(LOG_NOTICE, "New block detected on network from pool %d at height %d",
+			       pool->pool_no, height);
+		restart_threads();
+	} else {
+		if (memcmp(pool->prev_block, bedata, 32)) {
+			/* Work doesn't match what this pool has stored as
+			 * prev_block. Let's see if the work is from an old
+			 * block or the pool is just learning about a new
+			 * block. */
+			if (memcmp(bedata, current_block, 32)) {
+				/* Doesn't match current block. It's stale */
+				applog(LOG_DEBUG, "Stale data from pool %d at height %d", pool->pool_no, height);
+				ret = false;
+			} else {
+				/* Work is from new block and pool is up now
+				 * current. */
+				applog(LOG_INFO, "Pool %d now up to date at height %d", pool->pool_no, height);
+				cg_memcpy(pool->prev_block, bedata, 32);
+			}
+		}
+#if 0
+		/* This isn't ideal, this pool is still on an old block but
+		 * accepting shares from it. To maintain fair work distribution
+		 * we work on it anyway. */
+		if (memcmp(bedata, current_block, 32))
+			applog(LOG_DEBUG, "Pool %d still on old block", pool->pool_no);
+#endif
+		if (work->longpoll) {
+			work->work_block = ++work_block;
+			if (shared_strategy() || work->pool == current_pool()) {
+				if (work->stratum) {
+					applog(LOG_NOTICE, "Stratum from pool %d requested work restart",
+					       pool->pool_no);
+				} else {
+					applog(LOG_NOTICE, "%sLONGPOLL from pool %d requested work restart",
+					       work->gbt ? "GBT " : "", pool->pool_no);
+				}
+				restart_threads();
+			}
+		}
+	}
+out:
+	work->longpoll = false;
+
+	return ret;
+}
+
+static int tv_sort(struct work *worka, struct work *workb)
+{
+	return worka->tv_staged.tv_sec - workb->tv_staged.tv_sec;
+}
+
+static bool work_rollable(struct work *work)
+{
+	return (!work->clone && work->rolltime);
+}
+
+static bool hash_push(struct work *work)
+{
+	bool rc = true;
+
+	mutex_lock(stgd_lock);
+	if (work_rollable(work))
+		staged_rollable++;
+	if (likely(!getq->frozen)) {
+		HASH_ADD_INT(staged_work, id, work);
+		HASH_SORT(staged_work, tv_sort);
+	} else
+		rc = false;
+	pthread_cond_broadcast(&getq->cond);
+	mutex_unlock(stgd_lock);
+
+	return rc;
+}
+
+static void _stage_work(struct work *work)
+{
+	applog(LOG_DEBUG, "Pushing work from pool %d to hash queue", work->pool->pool_no);
+	work->work_block = work_block;
+	test_work_current(work);
+	work->pool->works++;
+	hash_push(work);
+}
+
+#ifdef HAVE_CURSES
+int curses_int(const char *query)
+{
+	int ret;
+	char *cvar;
+
+	cvar = curses_input(query);
+	ret = atoi(cvar);
+	free(cvar);
+	return ret;
+}
+#endif
+
+#ifdef HAVE_CURSES
+static bool input_pool(bool live);
+#endif
+
+#ifdef HAVE_CURSES
+static void display_pool_summary(struct pool *pool)
+{
+	if (curses_active_locked()) {
+		wlog("Pool: %s\n", pool->rpc_url);
+		if (pool->solved)
+			wlog("SOLVED %d BLOCK%s!\n", pool->solved, pool->solved > 1 ? "S" : "");
+		if (!pool->has_stratum)
+			wlog("%s own long-poll support\n", pool->hdr_path ? "Has" : "Does not have");
+		wlog(" Work templates received: %d\n", pool->getwork_requested);
+		wlog(" Share submissions: %"PRId64"\n", pool->accepted + pool->rejected);
+		wlog(" Accepted shares: %"PRId64"\n", pool->accepted);
+		wlog(" Rejected shares: %"PRId64"\n", pool->rejected);
+		wlog(" Accepted difficulty shares: %1.f\n", pool->diff_accepted);
+		wlog(" Rejected difficulty shares: %1.f\n", pool->diff_rejected);
+		if (pool->accepted || pool->rejected)
+			wlog(" Reject ratio: %.1f%%\n", (double)(pool->rejected * 100) / (double)(pool->accepted + pool->rejected));
+
+		wlog(" Items worked on: %d\n", pool->works);
+		wlog(" Discarded work due to new blocks: %d\n", pool->discarded_work);
+		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);
+		unlock_curses();
+	}
+}
+#endif
+
+/* We can't remove the memory used for this struct pool because there may
+ * still be work referencing it. We just remove it from the pools list */
+void remove_pool(struct pool *pool)
+{
+	int i, last_pool = total_pools - 1;
+	struct pool *other;
+
+	/* Boost priority of any lower prio than this one */
+	for (i = 0; i < total_pools; i++) {
+		other = pools[i];
+		if (other->prio > pool->prio)
+			other->prio--;
+	}
+
+	if (pool->pool_no < last_pool) {
+		/* Swap the last pool for this one */
+		(pools[last_pool])->pool_no = pool->pool_no;
+		pools[pool->pool_no] = pools[last_pool];
+	}
+	/* Give it an invalid number */
+	pool->pool_no = total_pools;
+	pool->removed = true;
+	total_pools--;
+}
+
+/* add a mutex if this needs to be thread safe in the future */
+static struct JE {
+	char *buf;
+	struct JE *next;
+} *jedata = NULL;
+
+static void json_escape_free()
+{
+	struct JE *jeptr = jedata;
+	struct JE *jenext;
+
+	jedata = NULL;
+
+	while (jeptr) {
+		jenext = jeptr->next;
+		free(jeptr->buf);
+		free(jeptr);
+		jeptr = jenext;
+	}
+}
+
+static char *json_escape(char *str)
+{
+	struct JE *jeptr;
+	char *buf, *ptr;
+
+	/* 2x is the max, may as well just allocate that */
+	ptr = buf = cgmalloc(strlen(str) * 2 + 1);
+	jeptr = cgmalloc(sizeof(*jeptr));
+	jeptr->buf = buf;
+	jeptr->next = jedata;
+	jedata = jeptr;
+
+	while (*str) {
+		if (*str == '\\' || *str == '"')
+			*(ptr++) = '\\';
+
+		*(ptr++) = *(str++);
+	}
+
+	*ptr = '\0';
+
+	return buf;
+}
+
+void write_config(FILE *fcfg)
+{
+	struct opt_table *opt;
+	int i;
+
+	/* Write pool values */
+	fputs("{\n\"pools\" : [", fcfg);
+	for(i = 0; i < total_pools; i++) {
+		struct pool *pool = priority_pool(i);
+
+		if (pool->quota != 1) {
+			fprintf(fcfg, "%s\n\t{\n\t\t\"quota\" : \"%s%s%s%d;%s\",", i > 0 ? "," : "",
+				pool->rpc_proxy ? json_escape((char *)proxytype(pool->rpc_proxytype)) : "",
+				pool->rpc_proxy ? json_escape(pool->rpc_proxy) : "",
+				pool->rpc_proxy ? "|" : "",
+				pool->quota,
+				json_escape(pool->rpc_url));
+		} else {
+			fprintf(fcfg, "%s\n\t{\n\t\t\"url\" : \"%s%s%s%s\",", i > 0 ? "," : "",
+				pool->rpc_proxy ? json_escape((char *)proxytype(pool->rpc_proxytype)) : "",
+				pool->rpc_proxy ? json_escape(pool->rpc_proxy) : "",
+				pool->rpc_proxy ? "|" : "",
+				json_escape(pool->rpc_url));
+		}
+#ifdef USE_XTRANONCE
+		if (pool->extranonce_subscribe)
+		fputs("\n\t\t\"extranonce-subscribe\" : true,", fcfg);
+#endif
+		fprintf(fcfg, "\n\t\t\"user\" : \"%s\",", json_escape(pool->rpc_user));
+		fprintf(fcfg, "\n\t\t\"pass\" : \"%s\"\n\t}", json_escape(pool->rpc_pass));
+		}
+	fputs("\n]\n", fcfg);
+
+	/* Simple bool,int and char options */
+	for (opt = opt_config_table; opt->type != OPT_END; opt++) {
+		char *p, *name = strdup(opt->names);
+
+		for (p = strtok(name, "|"); p; p = strtok(NULL, "|")) {
+			if (p[1] != '-')
+				continue;
+
+			if (opt->desc == opt_hidden)
+				continue;
+
+			if (opt->type & OPT_NOARG &&
+			   ((void *)opt->cb == (void *)opt_set_bool || (void *)opt->cb == (void *)opt_set_invbool) &&
+			   (*(bool *)opt->u.arg == ((void *)opt->cb == (void *)opt_set_bool))) {
+				fprintf(fcfg, ",\n\"%s\" : true", p+2);
+				continue;
+			}
+
+			if (opt->type & OPT_HASARG &&
+			    ((void *)opt->cb_arg == (void *)opt_set_intval ||
+			     (void *)opt->cb_arg == (void *)set_int_0_to_9999 ||
+			     (void *)opt->cb_arg == (void *)set_int_0_to_65535 ||
+			     (void *)opt->cb_arg == (void *)set_int_1_to_65535 ||
+			     (void *)opt->cb_arg == (void *)set_int_0_to_5 ||
+			     (void *)opt->cb_arg == (void *)set_int_0_to_10 ||
+			     (void *)opt->cb_arg == (void *)set_int_1_to_10 ||
+			     (void *)opt->cb_arg == (void *)set_int_24_to_32 ||
+			     (void *)opt->cb_arg == (void *)set_int_0_to_100 ||
+			     (void *)opt->cb_arg == (void *)set_int_0_to_255 ||
+			     (void *)opt->cb_arg == (void *)set_int_1_to_255 ||
+			     (void *)opt->cb_arg == (void *)set_int_0_to_7680 ||
+			     (void *)opt->cb_arg == (void *)set_int_0_to_200 ||
+			     (void *)opt->cb_arg == (void *)set_int_0_to_4 ||
+			     (void *)opt->cb_arg == (void *)set_int_32_to_63 ||
+			     (void *)opt->cb_arg == (void *)set_int_22_to_75 ||
+#ifdef USE_DRAGONMINT_T1
+			     (void *)opt->cb_arg == (void *)set_int_voltage ||
+			     (void *)opt->cb_arg == (void *)set_int_1_to_31 ||
+#endif
+#ifdef USE_AVALON4
+			     (void *)opt->cb_arg == (void *)set_int_1_to_60 ||
+#endif
+			     (void *)opt->cb_arg == (void *)set_int_42_to_85)) {
+				fprintf(fcfg, ",\n\"%s\" : \"%d\"", p+2, *(int *)opt->u.arg);
+				continue;
+			}
+
+			if (opt->type & OPT_HASARG &&
+			    ((void *)opt->cb_arg == (void *)set_float_0_to_500 ||
+			     (void *)opt->cb_arg == (void *)set_float_125_to_500 ||
+			     (void *)opt->cb_arg == (void *)set_float_100_to_250 ||
+			     (void *)opt->cb_arg == (void *)set_float_100_to_500)) {
+				fprintf(fcfg, ",\n\"%s\" : \"%.1f\"", p+2, *(float *)opt->u.arg);
+				continue;
+			}
+
+			if (opt->type & (OPT_HASARG | OPT_PROCESSARG) &&
+			    (opt->u.arg != &opt_set_null)) {
+				char *carg = *(char **)opt->u.arg;
+
+				if (carg)
+					fprintf(fcfg, ",\n\"%s\" : \"%s\"", p+2, json_escape(carg));
+				continue;
+			}
+		}
+		free(name);
+	}
+
+	/* Special case options */
+	if (pool_strategy == POOL_BALANCE)
+		fputs(",\n\"balance\" : true", fcfg);
+	if (pool_strategy == POOL_LOADBALANCE)
+		fputs(",\n\"load-balance\" : true", fcfg);
+	if (pool_strategy == POOL_ROUNDROBIN)
+		fputs(",\n\"round-robin\" : true", fcfg);
+	if (pool_strategy == POOL_ROTATE)
+		fprintf(fcfg, ",\n\"rotate\" : \"%d\"", opt_rotate_period);
+	fputs("\n}\n", fcfg);
+
+	json_escape_free();
+}
+
+void zero_bestshare(void)
+{
+	int i;
+
+	best_diff = 0;
+	memset(best_share, 0, 8);
+	suffix_string(best_diff, best_share, sizeof(best_share), 0);
+
+	for (i = 0; i < total_pools; i++) {
+		struct pool *pool = pools[i];
+		pool->best_diff = 0;
+	}
+}
+
+static struct timeval tv_hashmeter;
+static time_t hashdisplay_t;
+
+void zero_stats(void)
+{
+	int i;
+#ifdef USE_BITMAIN_SOC
+	struct sysinfo sInfo;
+	if (sysinfo(&sInfo))
+	{
+		applog(LOG_INFO, "Failed to get sysinfo, errno:%u, reason:%s\n",
+			   errno, strerror(errno));
+		total_tv_start_sys=time(NULL);
+	}
+	else
+	{
+		total_tv_start_sys=sInfo.uptime;
+	}
+
+#endif
+	cgtime(&total_tv_start);
+	copy_time(&tv_hashmeter, &total_tv_start);
+	total_rolling = 0;
+	rolling1 = 0;
+	rolling5 = 0;
+	rolling15 = 0;
+	total_mhashes_done = 0;
+#ifdef USE_BITMAIN_SOC
+	new_total_mhashes_done = 0;
+#endif
+	total_getworks = 0;
+	total_accepted = 0;
+	total_rejected = 0;
+	hw_errors = 0;
+	total_stale = 0;
+	total_discarded = 0;
+	local_work = 0;
+	total_go = 0;
+	total_ro = 0;
+	total_secs = 1.0;
+#ifdef USE_BITMAIN_SOC
+	new_total_secs = 1.0;
+#endif
+	total_diff1 = 0;
+	found_blocks = 0;
+	total_diff_accepted = 0;
+	total_diff_rejected = 0;
+	total_diff_stale = 0;
+
+	for (i = 0; i < total_pools; i++) {
+		struct pool *pool = pools[i];
+
+		pool->getwork_requested = 0;
+		pool->accepted = 0;
+		pool->rejected = 0;
+		pool->stale_shares = 0;
+		pool->discarded_work = 0;
+		pool->getfail_occasions = 0;
+		pool->remotefail_occasions = 0;
+		pool->last_share_time = 0;
+		pool->diff1 = 0;
+		pool->diff_accepted = 0;
+		pool->diff_rejected = 0;
+		pool->diff_stale = 0;
+		pool->last_share_diff = 0;
+	}
+
+	zero_bestshare();
+
+	for (i = 0; i < total_devices; ++i) {
+		struct cgpu_info *cgpu = get_devices(i);
+
+		copy_time(&cgpu->dev_start_tv, &total_tv_start);
+
+		mutex_lock(&hash_lock);
+		cgpu->total_mhashes = 0;
+		cgpu->accepted = 0;
+		cgpu->rejected = 0;
+		cgpu->hw_errors = 0;
+		cgpu->utility = 0.0;
+		cgpu->last_share_pool_time = 0;
+		cgpu->diff1 = 0;
+		cgpu->diff_accepted = 0;
+		cgpu->diff_rejected = 0;
+		cgpu->last_share_diff = 0;
+		mutex_unlock(&hash_lock);
+
+		/* Don't take any locks in the driver zero stats function, as
+		 * it's called async from everything else and we don't want to
+		 * deadlock. */
+		cgpu->drv->zero_stats(cgpu);
+	}
+}
+
+static void __maybe_unused set_highprio(void)
+{
+#ifndef WIN32
+	int ret = nice(-10);
+
+	if (!ret)
+		applog(LOG_DEBUG, "Unable to set thread to high priority");
+#else
+	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+#endif
+}
+
+static void __maybe_unused set_lowprio(void)
+{
+#ifndef WIN32
+	int ret = nice(10);
+
+	if (!ret)
+		applog(LOG_INFO, "Unable to set thread to low priority");
+#else
+	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
+#endif
+}
+
+#ifdef HAVE_CURSES
+static void display_pools(void)
+{
+	struct pool *pool;
+	int selected, i;
+	char input;
+
+	opt_loginput = true;
+	immedok(logwin, true);
+	clear_logwin();
+updated:
+	for (i = 0; i < total_pools; i++) {
+		pool = pools[i];
+
+		if (pool == current_pool())
+			wattron(logwin, A_BOLD);
+		if (pool->enabled != POOL_ENABLED)
+			wattron(logwin, A_DIM);
+		wlogprint("%d: ", pool->pool_no);
+		switch (pool->enabled) {
+			case POOL_ENABLED:
+				wlogprint("Enabled ");
+				break;
+			case POOL_DISABLED:
+				wlogprint("Disabled ");
+				break;
+			case POOL_REJECTING:
+				wlogprint("Rejecting ");
+				break;
+		}
+		wlogprint("%s Quota %d Prio %d: %s  User:%s\n",
+			pool->idle? "Dead" : "Alive",
+			pool->quota,
+			pool->prio,
+			pool->rpc_url, pool->rpc_user);
+		wattroff(logwin, A_BOLD | A_DIM);
+	}
+retry:
+	wlogprint("\nCurrent pool management strategy: %s\n",
+		strategies[pool_strategy].s);
+	if (pool_strategy == POOL_ROTATE)
+		wlogprint("Set to rotate every %d minutes\n", opt_rotate_period);
+	wlogprint("Pool [A]dd [R]emove [D]isable [E]nable [Q]uota change\n");
+	wlogprint("[C]hange management strategy [S]witch pool [I]nformation\n");
+	wlogprint("Or press any other key to continue\n");
+	logwin_update();
+	input = getch();
+
+	if (!strncasecmp(&input, "a", 1)) {
+		input_pool(true);
+		goto updated;
+	} else if (!strncasecmp(&input, "r", 1)) {
+		if (total_pools <= 1) {
+			wlogprint("Cannot remove last pool");
+			goto retry;
+		}
+		selected = curses_int("Select pool number");
+		if (selected < 0 || selected >= total_pools) {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		pool = pools[selected];
+		if (pool == current_pool())
+			switch_pools(NULL);
+		if (pool == current_pool()) {
+			wlogprint("Unable to remove pool due to activity\n");
+			goto retry;
+		}
+		disable_pool(pool);
+		remove_pool(pool);
+		goto updated;
+	} else if (!strncasecmp(&input, "s", 1)) {
+		selected = curses_int("Select pool number");
+		if (selected < 0 || selected >= total_pools) {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		pool = pools[selected];
+		enable_pool(pool);
+		switch_pools(pool);
+		goto updated;
+	} else if (!strncasecmp(&input, "d", 1)) {
+		if (enabled_pools <= 1) {
+			wlogprint("Cannot disable last pool");
+			goto retry;
+		}
+		selected = curses_int("Select pool number");
+		if (selected < 0 || selected >= total_pools) {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		pool = pools[selected];
+		disable_pool(pool);
+		if (pool == current_pool())
+			switch_pools(NULL);
+		goto updated;
+	} else if (!strncasecmp(&input, "e", 1)) {
+		selected = curses_int("Select pool number");
+		if (selected < 0 || selected >= total_pools) {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		pool = pools[selected];
+		enable_pool(pool);
+		if (pool->prio < current_pool()->prio)
+			switch_pools(pool);
+		goto updated;
+	} else if (!strncasecmp(&input, "c", 1)) {
+		for (i = 0; i <= TOP_STRATEGY; i++)
+			wlogprint("%d: %s\n", i, strategies[i].s);
+		selected = curses_int("Select strategy number type");
+		if (selected < 0 || selected > TOP_STRATEGY) {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		if (selected == POOL_ROTATE) {
+			opt_rotate_period = curses_int("Select interval in minutes");
+
+			if (opt_rotate_period < 0 || opt_rotate_period > 9999) {
+				opt_rotate_period = 0;
+				wlogprint("Invalid selection\n");
+				goto retry;
+			}
+		}
+		pool_strategy = selected;
+		switch_pools(NULL);
+		goto updated;
+	} else if (!strncasecmp(&input, "i", 1)) {
+		selected = curses_int("Select pool number");
+		if (selected < 0 || selected >= total_pools) {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		pool = pools[selected];
+		display_pool_summary(pool);
+		goto retry;
+	} else if (!strncasecmp(&input, "q", 1)) {
+		selected = curses_int("Select pool number");
+		if (selected < 0 || selected >= total_pools) {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		pool = pools[selected];
+		selected = curses_int("Set quota");
+		if (selected < 0) {
+			wlogprint("Invalid negative quota\n");
+			goto retry;
+		}
+		pool->quota = selected;
+		adjust_quota_gcd();
+		goto updated;
+	} else
+		clear_logwin();
+
+	immedok(logwin, false);
+	opt_loginput = false;
+}
+
+static void display_options(void)
+{
+	int selected;
+	char input;
+
+	opt_loginput = true;
+	immedok(logwin, true);
+	clear_logwin();
+retry:
+	wlogprint("[N]ormal [C]lear [S]ilent mode (disable all output)\n");
+	wlogprint("[D]ebug:%s\n[P]er-device:%s\n[Q]uiet:%s\n[V]erbose:%s\n"
+		  "[R]PC debug:%s\n[W]orkTime details:%s\nco[M]pact: %s\n"
+		  "[T]oggle status switching:%s\n"
+		  "w[I]descreen:%s\n"
+		  "[Z]ero statistics\n"
+		  "[L]og interval:%d\n",
+		opt_debug ? "on" : "off",
+	        want_per_device_stats? "on" : "off",
+		opt_quiet ? "on" : "off",
+		opt_log_output ? "on" : "off",
+		opt_protocol ? "on" : "off",
+		opt_worktime ? "on" : "off",
+		opt_compact ? "on" : "off",
+		switch_status ? "enabled" : "disabled",
+		opt_widescreen ? "enabled" : "disabled",
+		opt_log_interval);
+	wlogprint("Select an option or any other key to return\n");
+	logwin_update();
+	input = getch();
+	if (!strncasecmp(&input, "q", 1)) {
+		opt_quiet ^= true;
+		wlogprint("Quiet mode %s\n", opt_quiet ? "enabled" : "disabled");
+		goto retry;
+	} else if (!strncasecmp(&input, "v", 1)) {
+		opt_log_output ^= true;
+		if (opt_log_output)
+			opt_quiet = false;
+		wlogprint("Verbose mode %s\n", opt_log_output ? "enabled" : "disabled");
+		goto retry;
+	} else if (!strncasecmp(&input, "n", 1)) {
+		opt_log_output = false;
+		opt_debug = false;
+		opt_quiet = false;
+		opt_protocol = false;
+		opt_compact = false;
+		want_per_device_stats = false;
+		wlogprint("Output mode reset to normal\n");
+		switch_logsize(false);
+		goto retry;
+	} else if (!strncasecmp(&input, "d", 1)) {
+		opt_debug ^= true;
+		opt_log_output = opt_debug;
+		if (opt_debug)
+			opt_quiet = false;
+		wlogprint("Debug mode %s\n", opt_debug ? "enabled" : "disabled");
+		goto retry;
+	} else if (!strncasecmp(&input, "m", 1)) {
+		opt_compact ^= true;
+		wlogprint("Compact mode %s\n", opt_compact ? "enabled" : "disabled");
+		switch_logsize(false);
+		goto retry;
+	} else if (!strncasecmp(&input, "p", 1)) {
+		want_per_device_stats ^= true;
+		opt_log_output = want_per_device_stats;
+		wlogprint("Per-device stats %s\n", want_per_device_stats ? "enabled" : "disabled");
+		goto retry;
+	} else if (!strncasecmp(&input, "r", 1)) {
+		opt_protocol ^= true;
+		if (opt_protocol)
+			opt_quiet = false;
+		wlogprint("RPC protocol debugging %s\n", opt_protocol ? "enabled" : "disabled");
+		goto retry;
+	} else if (!strncasecmp(&input, "c", 1))
+		clear_logwin();
+	else if (!strncasecmp(&input, "l", 1)) {
+		selected = curses_int("Interval in seconds");
+		if (selected < 0 || selected > 9999) {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		opt_log_interval = selected;
+		wlogprint("Log interval set to %d seconds\n", opt_log_interval);
+		goto retry;
+	} else if (!strncasecmp(&input, "s", 1)) {
+		opt_realquiet = true;
+	} else if (!strncasecmp(&input, "w", 1)) {
+		opt_worktime ^= true;
+		wlogprint("WorkTime details %s\n", opt_worktime ? "enabled" : "disabled");
+		goto retry;
+	} else if (!strncasecmp(&input, "t", 1)) {
+		switch_status ^= true;
+		goto retry;
+	} else if (!strncasecmp(&input, "i", 1)) {
+		opt_widescreen ^= true;
+		goto retry;
+	} else if (!strncasecmp(&input, "z", 1)) {
+		zero_stats();
+		goto retry;
+	} else
+		clear_logwin();
+
+	immedok(logwin, false);
+	opt_loginput = false;
+}
+#endif
+
+void default_save_file(char *filename)
+{
+	if (default_config && *default_config) {
+		strcpy(filename, default_config);
+		return;
+	}
+
+#if defined(unix) || defined(__APPLE__)
+	if (getenv("HOME") && *getenv("HOME")) {
+	        strcpy(filename, getenv("HOME"));
+		strcat(filename, "/");
+	}
+	else
+		strcpy(filename, "");
+	strcat(filename, ".cgminer/");
+	mkdir(filename, 0777);
+#else
+	strcpy(filename, "");
+#endif
+	strcat(filename, def_conf);
+}
+
+#ifdef HAVE_CURSES
+static void set_options(void)
+{
+	char input;
+
+	opt_loginput = true;
+	immedok(logwin, true);
+	clear_logwin();
+retry:
+	wlogprint("[W]rite config file\n[C]gminer restart\n");
+	wlogprint("Select an option or any other key to return\n");
+	logwin_update();
+	input = getch();
+
+	if  (!strncasecmp(&input, "w", 1)) {
+		FILE *fcfg;
+		char *str, filename[PATH_MAX], prompt[PATH_MAX + 50];
+
+		default_save_file(filename);
+		snprintf(prompt, sizeof(prompt), "Config filename to write (Enter for default) [%s]", filename);
+		str = curses_input(prompt);
+		if (strcmp(str, "-1")) {
+			struct stat statbuf;
+
+			strcpy(filename, str);
+			free(str);
+			if (!stat(filename, &statbuf)) {
+				wlogprint("File exists, overwrite?\n");
+				input = getch();
+				if (strncasecmp(&input, "y", 1))
+					goto retry;
+			}
+		}
+		else
+			free(str);
+		fcfg = fopen(filename, "w");
+		if (!fcfg) {
+			wlogprint("Cannot open or create file\n");
+			goto retry;
+		}
+		write_config(fcfg);
+		fclose(fcfg);
+		goto retry;
+
+	} else if (!strncasecmp(&input, "c", 1)) {
+		wlogprint("Are you sure?\n");
+		input = getch();
+		if (!strncasecmp(&input, "y", 1))
+			app_restart();
+		else
+			clear_logwin();
+	} else
+		clear_logwin();
+
+	immedok(logwin, false);
+	opt_loginput = false;
+}
+
+#ifdef USE_USBUTILS
+static void mt_enable(struct thr_info *mythr)
+{
+	cgsem_post(&mythr->sem);
+}
+
+static void set_usb(void)
+{
+	int selected, i, mt, enabled, disabled, zombie, total, blacklisted;
+	struct cgpu_info *cgpu;
+	struct thr_info *thr;
+	double val;
+	char input;
+
+	opt_loginput = true;
+	immedok(logwin, true);
+	clear_logwin();
+
+retry:
+	enabled = 0;
+	disabled = 0;
+	zombie = 0;
+	total = 0;
+	blacklisted = 0;
+
+	rd_lock(&mining_thr_lock);
+	mt = mining_threads;
+	rd_unlock(&mining_thr_lock);
+
+	for (i = 0; i < mt; i++) {
+		cgpu = mining_thr[i]->cgpu;
+		if (unlikely(!cgpu))
+			continue;
+		if (cgpu->usbinfo.nodev)
+			zombie++;
+		else if  (cgpu->deven == DEV_DISABLED)
+			disabled++;
+		else
+			enabled++;
+		if (cgpu->blacklisted)
+			blacklisted++;
+		total++;
+	}
+	wlogprint("Hotplug interval:%d\n", hotplug_time);
+	wlogprint("%d USB devices, %d enabled, %d disabled, %d zombie\n",
+		  total, enabled, disabled, zombie);
+	wlogprint("[S]ummary of device information\n");
+	wlogprint("[E]nable device\n");
+	wlogprint("[D]isable device\n");
+	wlogprint("[U]nplug to allow hotplug restart\n");
+	wlogprint("[R]eset device USB\n");
+	wlogprint("[L]ist all known devices\n");
+	wlogprint("[B]lacklist current device from current instance of cgminer\n");
+	wlogprint("[W]hitelist previously blacklisted device\n");
+	wlogprint("[H]otplug interval (0 to disable)\n");
+	wlogprint("Select an option or any other key to return\n");
+	logwin_update();
+	input = getch();
+
+	if (!strncasecmp(&input, "s", 1)) {
+		selected = curses_int("Select device number");
+		if (selected < 0 || selected >= mt)  {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		cgpu = mining_thr[selected]->cgpu;
+		wlogprint("Device %03u:%03u\n", cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address);
+		wlogprint("Name %s\n", cgpu->drv->name);
+		wlogprint("ID %d\n", cgpu->device_id);
+		wlogprint("Enabled: %s\n", cgpu->deven != DEV_DISABLED ? "Yes" : "No");
+		wlogprint("Temperature %.1f\n", cgpu->temp);
+		wlogprint("MHS av %.0f\n", cgpu->total_mhashes / cgpu_runtime(cgpu));
+		wlogprint("Accepted %d\n", cgpu->accepted);
+		wlogprint("Rejected %d\n", cgpu->rejected);
+		wlogprint("Hardware Errors %d\n", cgpu->hw_errors);
+		wlogprint("Last Share Pool %d\n", cgpu->last_share_pool_time > 0 ? cgpu->last_share_pool : -1);
+		wlogprint("Total MH %.1f\n", cgpu->total_mhashes);
+		wlogprint("Diff1 Work %"PRId64"\n", cgpu->diff1);
+		wlogprint("Difficulty Accepted %.1f\n", cgpu->diff_accepted);
+		wlogprint("Difficulty Rejected %.1f\n", cgpu->diff_rejected);
+		wlogprint("Last Share Difficulty %.1f\n", cgpu->last_share_diff);
+		wlogprint("No Device: %s\n", cgpu->usbinfo.nodev ? "True" : "False");
+		wlogprint("Last Valid Work %"PRIu64"\n", (uint64_t)cgpu->last_device_valid_work);
+		val = 0;
+		if (cgpu->hw_errors + cgpu->diff1)
+			val = cgpu->hw_errors / (cgpu->hw_errors + cgpu->diff1);
+		wlogprint("Device Hardware %.1f%%\n", val);
+		val = 0;
+		if (cgpu->diff1)
+			val = cgpu->diff_rejected / cgpu->diff1;
+		wlogprint("Device Rejected %.1f%%\n", val);
+		goto retry;
+	} else if (!strncasecmp(&input, "e", 1)) {
+		selected = curses_int("Select device number");
+		if (selected < 0 || selected >= mt)  {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		cgpu = mining_thr[selected]->cgpu;
+		if (cgpu->usbinfo.nodev) {
+			wlogprint("Device removed, unable to re-enable!\n");
+			goto retry;
+		}
+		thr = get_thread(selected);
+		cgpu->deven = DEV_ENABLED;
+		mt_enable(thr);
+		goto retry;
+	} else if (!strncasecmp(&input, "d", 1)) {
+		selected = curses_int("Select device number");
+		if (selected < 0 || selected >= mt)  {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		cgpu = mining_thr[selected]->cgpu;
+		cgpu->deven = DEV_DISABLED;
+		goto retry;
+	} else if (!strncasecmp(&input, "u", 1)) {
+		selected = curses_int("Select device number");
+		if (selected < 0 || selected >= mt)  {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		cgpu = mining_thr[selected]->cgpu;
+		if (cgpu->usbinfo.nodev) {
+			wlogprint("Device already removed, unable to unplug!\n");
+			goto retry;
+		}
+		usb_nodev(cgpu);
+		goto retry;
+	} else if (!strncasecmp(&input, "r", 1)) {
+		selected = curses_int("Select device number");
+		if (selected < 0 || selected >= mt)  {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		cgpu = mining_thr[selected]->cgpu;
+		if (cgpu->usbinfo.nodev) {
+			wlogprint("Device already removed, unable to reset!\n");
+			goto retry;
+		}
+		usb_reset(cgpu);
+		goto retry;
+	} else if (!strncasecmp(&input, "b", 1)) {
+		selected = curses_int("Select device number");
+		if (selected < 0 || selected >= mt)  {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		cgpu = mining_thr[selected]->cgpu;
+		if (cgpu->usbinfo.nodev) {
+			wlogprint("Device already removed, unable to blacklist!\n");
+			goto retry;
+		}
+		blacklist_cgpu(cgpu);
+		goto retry;
+	} else if (!strncasecmp(&input, "w", 1)) {
+		if (!blacklisted) {
+			wlogprint("No blacklisted devices!\n");
+			goto retry;
+		}
+		wlogprint("Blacklisted devices:\n");
+		for (i = 0; i < mt; i++) {
+			cgpu = mining_thr[i]->cgpu;
+			if (unlikely(!cgpu))
+				continue;
+			if (cgpu->blacklisted) {
+				wlogprint("%d: %s %d %03u:%03u\n", i, cgpu->drv->name,
+					  cgpu->device_id, cgpu->usbinfo.bus_number,
+					  cgpu->usbinfo.device_address);
+			}
+		}
+		selected = curses_int("Select device number");
+		if (selected < 0 || selected >= mt)  {
+			wlogprint("Invalid selection\n");
+			goto retry;
+		}
+		cgpu = mining_thr[selected]->cgpu;
+		if (!cgpu->blacklisted) {
+			wlogprint("Device not blacklisted, unable to whitelist\n");
+			goto retry;
+		}
+		whitelist_cgpu(cgpu);
+		goto retry;
+	} else if (!strncasecmp(&input, "h", 1)) {
+		selected = curses_int("Select hotplug interval in seconds (0 to disable)");
+		if (selected < 0 || selected > 9999)  {
+			wlogprint("Invalid value\n");
+			goto retry;
+		}
+		hotplug_time = selected;
+		goto retry;
+	} else if (!strncasecmp(&input, "l", 1)) {
+		usb_list();
+		goto retry;
+	} else
+		clear_logwin();
+
+	immedok(logwin, false);
+	opt_loginput = false;
+}
+#endif
+
+static void *input_thread(void __maybe_unused *userdata)
+{
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+	RenameThread("Input");
+
+	if (!curses_active)
+		return NULL;
+
+	while (1) {
+		char input;
+
+		input = getch();
+		if (!strncasecmp(&input, "q", 1)) {
+			kill_work();
+			return NULL;
+		} else if (!strncasecmp(&input, "d", 1))
+			display_options();
+		else if (!strncasecmp(&input, "p", 1))
+			display_pools();
+		else if (!strncasecmp(&input, "s", 1))
+			set_options();
+#ifdef USE_USBUTILS
+		else if (!strncasecmp(&input, "u", 1))
+			set_usb();
+#endif
+		if (opt_realquiet) {
+			disable_curses();
+			break;
+		}
+	}
+
+	return NULL;
+}
+#endif
+
+static void *api_thread(void *userdata)
+{
+	struct thr_info *mythr = userdata;
+
+	pthread_detach(pthread_self());
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+	RenameThread("API");
+
+	set_lowprio();
+	api(api_thr_id);
+
+	PTH(mythr) = 0L;
+
+	return NULL;
+}
+
+/* Sole work devices are serialised wrt calling get_work so they report in on
+ * each pass through their scanhash function as well as in get_work whereas
+ * queued work devices work asynchronously so get them to report in and out
+ * only across get_work. */
+static void thread_reportin(struct thr_info *thr)
+{
+	thr->getwork = false;
+	cgtime(&thr->last);
+	thr->cgpu->status = LIFE_WELL;
+	thr->cgpu->device_last_well = time(NULL);
+}
+
+/* Tell the watchdog thread this thread is waiting on get work and should not
+ * be restarted */
+static void thread_reportout(struct thr_info *thr)
+{
+	thr->getwork = true;
+	cgtime(&thr->last);
+	thr->cgpu->status = LIFE_WELL;
+	thr->cgpu->device_last_well = time(NULL);
+}
+
+static void hashmeter(int thr_id, uint64_t hashes_done)
+{
+	bool showlog = false;
+	double tv_tdiff;
+	time_t now_t;
+	int diff_t;
+
+#ifdef USE_BITMAIN_SOC
+	struct sysinfo sInfo;
+	if (sysinfo(&sInfo))
+	{
+		applog(LOG_INFO, "Failed to get sysinfo, errno:%u, reason:%s\n",
+			   errno, strerror(errno));
+		total_tv_end_sys=time(NULL);
+	}
+	else
+	{
+		total_tv_end_sys=sInfo.uptime;
+	}
+#endif
+
+	cgtime(&total_tv_end);
+	tv_tdiff = tdiff(&total_tv_end, &tv_hashmeter);
+	now_t = total_tv_end.tv_sec;
+	diff_t = now_t - hashdisplay_t;
+	if (diff_t >= opt_log_interval) {
+		alt_status ^= switch_status;
+		hashdisplay_t = now_t;
+		showlog = true;
+	} else if (thr_id < 0) {
+		/* hashmeter is called by non-mining threads in case nothing
+		 * has reported in to allow hashrate to converge to zero , but
+		 * we only update if it has been more than opt_log_interval */
+		return;
+	}
+	copy_time(&tv_hashmeter, &total_tv_end);
+
+	if (thr_id >= 0) {
+		struct thr_info *thr = get_thread(thr_id);
+		struct cgpu_info *cgpu = thr->cgpu;
+		double device_tdiff, thr_mhs;
+
+		/* Update the last time this thread reported in */
+		copy_time(&thr->last, &total_tv_end);
+		cgpu->device_last_well = now_t;
+		device_tdiff = tdiff(&total_tv_end, &cgpu->last_message_tv);
+		copy_time(&cgpu->last_message_tv, &total_tv_end);
+		thr_mhs = (double)hashes_done / device_tdiff / 1000000;
+		applog(LOG_DEBUG, "[thread %d: %"PRIu64" hashes, %.1f mhash/sec]",
+		       thr_id, hashes_done, thr_mhs);
+		hashes_done /= 1000000;
+
+		mutex_lock(&hash_lock);
+		cgpu->total_mhashes += hashes_done;
+		decay_time(&cgpu->rolling, hashes_done, device_tdiff, opt_log_interval);
+		decay_time(&cgpu->rolling1, hashes_done, device_tdiff, 60.0);
+		decay_time(&cgpu->rolling5, hashes_done, device_tdiff, 300.0);
+		decay_time(&cgpu->rolling15, hashes_done, device_tdiff, 900.0);
+		mutex_unlock(&hash_lock);
+
+		if (want_per_device_stats && showlog) {
+			char logline[256];
+
+			get_statline(logline, sizeof(logline), cgpu);
+			if (!curses_active) {
+				printf("%s          \r", logline);
+				fflush(stdout);
+			} else
+				applog(LOG_INFO, "%s", logline);
+		}
+	} else {
+		/* No device has reported in, we have been called from the
+		 * watchdog thread so decay all the hashrates */
+		mutex_lock(&hash_lock);
+		for (thr_id = 0; thr_id < mining_threads; thr_id++) {
+			struct thr_info *thr = get_thread(thr_id);
+			struct cgpu_info *cgpu = thr->cgpu;
+			double device_tdiff  = tdiff(&total_tv_end, &cgpu->last_message_tv);
+
+			copy_time(&cgpu->last_message_tv, &total_tv_end);
+			decay_time(&cgpu->rolling, 0, device_tdiff, opt_log_interval);
+			decay_time(&cgpu->rolling1, 0, device_tdiff, 60.0);
+			decay_time(&cgpu->rolling5, 0, device_tdiff, 300.0);
+			decay_time(&cgpu->rolling15, 0, device_tdiff, 900.0);
+		}
+		mutex_unlock(&hash_lock);
+	}
+
+	mutex_lock(&hash_lock);
+	total_mhashes_done += hashes_done;
+	decay_time(&total_rolling, hashes_done, tv_tdiff, opt_log_interval);
+	decay_time(&rolling1, hashes_done, tv_tdiff, 60.0);
+	decay_time(&rolling5, hashes_done, tv_tdiff, 300.0);
+	decay_time(&rolling15, hashes_done, tv_tdiff, 900.0);
+	global_hashrate = llround(total_rolling) * 1000000;
+#ifndef USE_BITMAIN_SOC
+	total_secs = tdiff(&total_tv_end, &total_tv_start);
+#else
+	total_secs = total_tv_end_sys*1.0-total_tv_start_sys*1.0;
+#endif
+	if (showlog) {
+		char displayed_hashes[16], displayed_rolling[16];
+		char displayed_r1[16], displayed_r5[16], displayed_r15[16];
+		uint64_t d64;
+
+		d64 = (double)total_mhashes_done / total_secs * 1000000ull;
+		suffix_string(d64, displayed_hashes, sizeof(displayed_hashes), 4);
+		d64 = (double)total_rolling * 1000000ull;
+		suffix_string(d64, displayed_rolling, sizeof(displayed_rolling), 4);
+		d64 = (double)rolling1 * 1000000ull;
+		suffix_string(d64, displayed_r1, sizeof(displayed_rolling), 4);
+		d64 = (double)rolling5 * 1000000ull;
+		suffix_string(d64, displayed_r5, sizeof(displayed_rolling), 4);
+		d64 = (double)rolling15 * 1000000ull;
+		suffix_string(d64, displayed_r15, sizeof(displayed_rolling), 4);
+
+		snprintf(statusline, sizeof(statusline),
+			"(%ds):%s (1m):%s (5m):%s (15m):%s (avg):%sh/s",
+			opt_log_interval, displayed_rolling, displayed_r1, displayed_r5,
+			displayed_r15, displayed_hashes);
+	}
+	mutex_unlock(&hash_lock);
+
+#ifdef USE_LIBSYSTEMD
+	sd_notifyf(false, "STATUS=%s", statusline);
+#endif
+
+	if (showlog) {
+		if (!curses_active) {
+			printf("%s          \r", statusline);
+			fflush(stdout);
+		} else
+			applog(LOG_INFO, "%s", statusline);
+	}
+}
+
+static void stratum_share_result(json_t *val, json_t *res_val, json_t *err_val,
+				 struct stratum_share *sshare)
+{
+	struct work *work = sshare->work;
+	time_t now_t = time(NULL);
+	char hashshow[64];
+	int srdiff;
+
+	srdiff = now_t - sshare->sshare_sent;
+	if (opt_debug || srdiff > 0) {
+		applog(LOG_INFO, "Pool %d stratum share result lag time %d seconds",
+		       work->pool->pool_no, srdiff);
+	}
+	show_hash(work, hashshow);
+	share_result(val, res_val, err_val, work, hashshow, false, "");
+}
+
+/* Parses stratum json responses and tries to find the id that the request
+ * matched to and treat it accordingly. */
+static bool parse_stratum_response(struct pool *pool, char *s)
+{
+	json_t *val = NULL, *err_val, *res_val, *id_val;
+	struct stratum_share *sshare;
+	json_error_t err;
+	bool ret = false;
+	int id;
+
+	val = JSON_LOADS(s, &err);
+	if (!val) {
+		applog(LOG_INFO, "JSON decode failed(%d): %s", err.line, err.text);
+		goto out;
+	}
+
+	res_val = json_object_get(val, "result");
+	err_val = json_object_get(val, "error");
+	id_val = json_object_get(val, "id");
+
+	if (json_is_null(id_val) || !id_val) {
+		char *ss;
+
+		if (err_val)
+			ss = json_dumps(err_val, JSON_INDENT(3));
+		else
+			ss = strdup("(unknown reason)");
+
+		applog(LOG_INFO, "JSON-RPC non method decode failed: %s", ss);
+
+		free(ss);
+
+		goto out;
+	}
+
+	id = json_integer_value(id_val);
+
+	mutex_lock(&sshare_lock);
+	HASH_FIND_INT(stratum_shares, &id, sshare);
+	if (sshare) {
+		HASH_DEL(stratum_shares, sshare);
+		pool->sshares--;
+	}
+	mutex_unlock(&sshare_lock);
+
+	if (!sshare) {
+		double pool_diff;
+
+		if (!res_val)
+			goto out;
+		/* Since the share is untracked, we can only guess at what the
+		 * work difficulty is based on the current pool diff. */
+		cg_rlock(&pool->data_lock);
+		pool_diff = pool->sdiff;
+		cg_runlock(&pool->data_lock);
+
+		if (json_is_true(res_val)) {
+			applog(LOG_NOTICE, "Accepted untracked stratum share from pool %d", pool->pool_no);
+
+			/* We don't know what device this came from so we can't
+			 * attribute the work to the relevant cgpu */
+			mutex_lock(&stats_lock);
+			total_accepted++;
+			pool->accepted++;
+			total_diff_accepted += pool_diff;
+			pool->diff_accepted += pool_diff;
+			mutex_unlock(&stats_lock);
+		} else {
+			applog(LOG_NOTICE, "Rejected untracked stratum share from pool %d", pool->pool_no);
+
+			mutex_lock(&stats_lock);
+			total_rejected++;
+			pool->rejected++;
+			total_diff_rejected += pool_diff;
+			pool->diff_rejected += pool_diff;
+			mutex_unlock(&stats_lock);
+		}
+		goto out;
+	}
+	stratum_share_result(val, res_val, err_val, sshare);
+	free_work(sshare->work);
+	free(sshare);
+
+	ret = true;
+out:
+	if (val)
+		json_decref(val);
+
+	return ret;
+}
+
+void clear_stratum_shares(struct pool *pool)
+{
+	struct stratum_share *sshare, *tmpshare;
+	double diff_cleared = 0;
+	int cleared = 0;
+
+	mutex_lock(&sshare_lock);
+	HASH_ITER(hh, stratum_shares, sshare, tmpshare) {
+		if (sshare->work->pool == pool) {
+			HASH_DEL(stratum_shares, sshare);
+			diff_cleared += sshare->work->work_difficulty;
+			free_work(sshare->work);
+			pool->sshares--;
+			free(sshare);
+			cleared++;
+		}
+	}
+	mutex_unlock(&sshare_lock);
+
+	if (cleared) {
+		applog(LOG_WARNING, "Lost %d shares due to stratum disconnect on pool %d", cleared, pool->pool_no);
+		pool->stale_shares += cleared;
+		total_stale += cleared;
+		pool->diff_stale += diff_cleared;
+		total_diff_stale += diff_cleared;
+	}
+}
+
+void clear_pool_work(struct pool *pool)
+{
+	struct work *work, *tmp;
+	int cleared = 0;
+
+	mutex_lock(stgd_lock);
+	HASH_ITER(hh, staged_work, work, tmp) {
+		if (work->pool == pool) {
+			HASH_DEL(staged_work, work);
+			free_work(work);
+			cleared++;
+		}
+	}
+	mutex_unlock(stgd_lock);
+
+	if (cleared)
+		applog(LOG_INFO, "Cleared %d work items due to stratum disconnect on pool %d", cleared, pool->pool_no);
+}
+
+static int cp_prio(void)
+{
+	int prio;
+
+	cg_rlock(&control_lock);
+	prio = currentpool->prio;
+	cg_runlock(&control_lock);
+
+	return prio;
+}
+
+/* We only need to maintain a secondary pool connection when we need the
+ * capacity to get work from the backup pools while still on the primary */
+static bool cnx_needed(struct pool *pool)
+{
+	struct pool *cp;
+
+	if (pool->enabled != POOL_ENABLED)
+		return false;
+
+	/* Balance strategies need all pools online */
+	if (pool_strategy == POOL_BALANCE)
+		return true;
+	if (pool_strategy == POOL_LOADBALANCE)
+		return true;
+
+	/* Idle stratum pool needs something to kick it alive again */
+	if (pool->has_stratum && pool->idle)
+		return true;
+
+	cp = current_pool();
+	if (cp == pool)
+		return true;
+	/* If we're waiting for a response from shares submitted, keep the
+	 * connection open. */
+	if (pool->sshares)
+		return true;
+	/* If the pool has only just come to life and is higher priority than
+	 * the current pool keep the connection open so we can fail back to
+	 * it. */
+	if (pool_strategy == POOL_FAILOVER && pool->prio < cp_prio())
+		return true;
+	/* We've run out of work, bring anything back to life. */
+	if (no_work)
+		return true;
+	return false;
+}
+
+static void wait_lpcurrent(struct pool *pool);
+static void pool_resus(struct pool *pool);
+static void gen_stratum_work(struct pool *pool, struct work *work);
+
+void stratum_resumed(struct pool *pool)
+{
+	if (pool_tclear(pool, &pool->idle)) {
+		applog(LOG_INFO, "Stratum connection to pool %d resumed", pool->pool_no);
+		pool_resus(pool);
+	}
+}
+
+static bool supports_resume(struct pool *pool)
+{
+	bool ret;
+
+	cg_rlock(&pool->data_lock);
+	ret = (pool->sessionid != NULL);
+	cg_runlock(&pool->data_lock);
+
+	return ret;
+}
+
+/* One stratum receive thread per pool that has stratum waits on the socket
+ * checking for new messages and for the integrity of the socket connection. We
+ * reset the connection based on the integrity of the receive side only as the
+ * send side will eventually expire data it fails to send. */
+static void *stratum_rthread(void *userdata)
+{
+	struct pool *pool = (struct pool *)userdata;
+	char threadname[16];
+
+	pthread_detach(pthread_self());
+
+	snprintf(threadname, sizeof(threadname), "%d/RStratum", pool->pool_no);
+	RenameThread(threadname);
+
+	while (42) {
+		struct timeval timeout;
+		int sel_ret;
+		fd_set rd;
+		char *s;
+
+		if (unlikely(pool->removed)) {
+			suspend_stratum(pool);
+			break;
+		}
+
+		/* Check to see whether we need to maintain this connection
+		 * indefinitely or just bring it up when we switch to this
+		 * pool */
+		if (!sock_full(pool) && !cnx_needed(pool)) {
+			suspend_stratum(pool);
+			clear_stratum_shares(pool);
+			clear_pool_work(pool);
+
+			wait_lpcurrent(pool);
+			while (!restart_stratum(pool)) {
+				pool_died(pool);
+				if (pool->removed)
+					goto out;
+				cgsleep_ms(5000);
+			}
+		}
+
+		FD_ZERO(&rd);
+		FD_SET(pool->sock, &rd);
+		timeout.tv_sec = 90;
+		timeout.tv_usec = 0;
+
+		/* The protocol specifies that notify messages should be sent
+		 * every minute so if we fail to receive any for 90 seconds we
+		 * assume the connection has been dropped and treat this pool
+		 * as dead */
+		if (!sock_full(pool) && (sel_ret = select(pool->sock + 1, &rd, NULL, NULL, &timeout)) < 1) {
+			applog(LOG_DEBUG, "Stratum select failed on pool %d with value %d", pool->pool_no, sel_ret);
+			s = NULL;
+		} else
+			s = recv_line(pool);
+		if (!s) {
+			applog(LOG_NOTICE, "Stratum connection to pool %d interrupted", pool->pool_no);
+			pool->getfail_occasions++;
+			total_go++;
+
+			/* If the socket to our stratum pool disconnects, all
+			 * tracked submitted shares are lost and we will leak
+			 * the memory if we don't discard their records. */
+			if (!supports_resume(pool) || opt_lowmem)
+				clear_stratum_shares(pool);
+			clear_pool_work(pool);
+			if (pool == current_pool())
+				restart_threads();
+
+			while (!restart_stratum(pool)) {
+				pool_died(pool);
+				if (pool->removed)
+					goto out;
+				cgsleep_ms(5000);
+			}
+			continue;
+		}
+
+		/* Check this pool hasn't died while being a backup pool and
+		 * has not had its idle flag cleared */
+		stratum_resumed(pool);
+
+		if (!parse_method(pool, s) && !parse_stratum_response(pool, s))
+			applog(LOG_INFO, "Unknown stratum msg: %s", s);
+		else if (pool->swork.clean) {
+			struct work *work = make_work();
+
+			/* Generate a single work item to update the current
+			 * block database */
+			gen_stratum_work(pool, work);
+			/* Return value doesn't matter. We're just informing
+			 * that we may need to restart. */
+			test_work_current(work);
+			free_work(work);
+		}
+		free(s);
+	}
+
+out:
+	return NULL;
+}
+
+/* Each pool has one stratum send thread for sending shares to avoid many
+ * threads being created for submission since all sends need to be serialised
+ * anyway. */
+static void *stratum_sthread(void *userdata)
+{
+	struct pool *pool = (struct pool *)userdata;
+	uint64_t last_nonce2 = 0;
+	uint32_t last_nonce = 0;
+	char threadname[16];
+
+	pthread_detach(pthread_self());
+
+	snprintf(threadname, sizeof(threadname), "%d/SStratum", pool->pool_no);
+	RenameThread(threadname);
+
+	pool->stratum_q = tq_new();
+	if (!pool->stratum_q)
+		quit(1, "Failed to create stratum_q in stratum_sthread");
+
+	while (42) {
+		char noncehex[12], nonce2hex[20], s[1024];
+		struct stratum_share *sshare;
+		uint32_t *hash32, nonce;
+		unsigned char nonce2[8];
+		uint64_t *nonce2_64;
+		struct work *work;
+		bool submitted;
+
+		if (unlikely(pool->removed))
+			break;
+#if defined (USE_AVALON2) || defined (USE_AVALON4) || defined (USE_AVALON7) || defined (USE_AVALON8) || defined (USE_AVALON9) || defined (USE_AVALON_MINER) || defined (USE_HASHRATIO)
+		work = tq_pop(pool->stratum_q, NULL);
+#else
+work = tq_pop(pool->stratum_q);
+#endif
+		if (unlikely(!work))
+			quit(1, "Stratum q returned empty work");
+
+		if (unlikely(work->nonce2_len > 8)) {
+			applog(LOG_ERR, "Pool %d asking for inappropriately long nonce2 length %d",
+			       pool->pool_no, (int)work->nonce2_len);
+			applog(LOG_ERR, "Not attempting to submit shares");
+			free_work(work);
+			continue;
+		}
+
+		nonce = *((uint32_t *)(work->data + 76));
+		nonce2_64 = (uint64_t *)nonce2;
+		*nonce2_64 = htole64(work->nonce2);
+		/* Filter out duplicate shares */
+		if (unlikely(nonce == last_nonce && *nonce2_64 == last_nonce2)) {
+			applog(LOG_INFO, "Filtering duplicate share to pool %d",
+			       pool->pool_no);
+			free_work(work);
+			continue;
+		}
+		last_nonce = nonce;
+		last_nonce2 = *nonce2_64;
+		__bin2hex(noncehex, (const unsigned char *)&nonce, 4);
+		__bin2hex(nonce2hex, nonce2, work->nonce2_len);
+
+		sshare = cgcalloc(sizeof(struct stratum_share), 1);
+		hash32 = (uint32_t *)work->hash;
+		submitted = false;
+
+		sshare->sshare_time = time(NULL);
+		/* This work item is freed in parse_stratum_response */
+		sshare->work = work;
+		memset(s, 0, 1024);
+
+		mutex_lock(&sshare_lock);
+		/* Give the stratum share a unique id */
+		sshare->id = swork_id++;
+		mutex_unlock(&sshare_lock);
+
+		if (pool->vmask) {
+			snprintf(s, sizeof(s),
+				 "{\"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"], \"id\": %d, \"method\": \"mining.submit\"}",
+				pool->rpc_user, work->job_id, nonce2hex, work->ntime, noncehex, pool->vmask_002[work->micro_job_id], sshare->id);
+		} else {
+			snprintf(s, sizeof(s),
+				"{\"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"], \"id\": %d, \"method\": \"mining.submit\"}",
+				pool->rpc_user, work->job_id, nonce2hex, work->ntime, noncehex, sshare->id);
+		}
+
+		applog(LOG_INFO, "Submitting share %08lx to pool %d",
+					(long unsigned int)htole32(hash32[6]), pool->pool_no);
+
+		/* Try resubmitting for up to 2 minutes if we fail to submit
+		 * once and the stratum pool nonce1 still matches suggesting
+		 * we may be able to resume. */
+		while (time(NULL) < sshare->sshare_time + 120) {
+			bool sessionid_match;
+
+			if (likely(stratum_send(pool, s, strlen(s)))) {
+				mutex_lock(&sshare_lock);
+				HASH_ADD_INT(stratum_shares, id, sshare);
+				pool->sshares++;
+				mutex_unlock(&sshare_lock);
+
+				if (pool_tclear(pool, &pool->submit_fail))
+						applog(LOG_WARNING, "Pool %d communication resumed, submitting work", pool->pool_no);
+				applog(LOG_DEBUG, "Successfully submitted, adding to stratum_shares db");
+				submitted = true;
+				break;
+			}
+			if (!pool_tset(pool, &pool->submit_fail) && cnx_needed(pool)) {
+				applog(LOG_WARNING, "Pool %d stratum share submission failure", pool->pool_no);
+				total_ro++;
+				pool->remotefail_occasions++;
+			}
+
+			if (opt_lowmem) {
+				applog(LOG_DEBUG, "Lowmem option prevents resubmitting stratum share");
+				break;
+			}
+
+			cg_rlock(&pool->data_lock);
+			sessionid_match = (pool->nonce1 && !strcmp(work->nonce1, pool->nonce1));
+			cg_runlock(&pool->data_lock);
+
+			if (!sessionid_match) {
+				applog(LOG_DEBUG, "No matching session id for resubmitting stratum share");
+				break;
+			}
+			/* Retry every 5 seconds */
+			sleep(5);
+		}
+
+		if (unlikely(!submitted)) {
+			applog(LOG_DEBUG, "Failed to submit stratum share, discarding");
+			free_work(work);
+			free(sshare);
+			pool->stale_shares++;
+			total_stale++;
+		} else {
+			int ssdiff;
+
+			sshare->sshare_sent = time(NULL);
+			ssdiff = sshare->sshare_sent - sshare->sshare_time;
+			if (opt_debug || ssdiff > 0) {
+				applog(LOG_INFO, "Pool %d stratum share submission lag time %d seconds",
+				       pool->pool_no, ssdiff);
+			}
+		}
+	}
+
+	/* Freeze the work queue but don't free up its memory in case there is
+	 * work still trying to be submitted to the removed pool. */
+	tq_freeze(pool->stratum_q);
+
+	return NULL;
+}
+
+static void init_stratum_threads(struct pool *pool)
+{
+	have_longpoll = true;
+
+	if (unlikely(pthread_create(&pool->stratum_sthread, NULL, stratum_sthread, (void *)pool)))
+		quit(1, "Failed to create stratum sthread");
+	if (unlikely(pthread_create(&pool->stratum_rthread, NULL, stratum_rthread, (void *)pool)))
+		quit(1, "Failed to create stratum rthread");
+}
+
+static void *longpoll_thread(void *userdata);
+
+static bool stratum_works(struct pool *pool)
+{
+	applog(LOG_INFO, "Testing pool %d stratum %s", pool->pool_no, pool->stratum_url);
+	if (!extract_sockaddr(pool->stratum_url, &pool->sockaddr_url, &pool->stratum_port))
+		return false;
+
+	if (!initiate_stratum(pool))
+		return false;
+
+	return true;
+}
+
+#ifdef HAVE_LIBCURL
+static void __setup_gbt_solo(struct pool *pool)
+{
+	cg_wlock(&pool->gbt_lock);
+	cg_memcpy(pool->coinbase, scriptsig_header_bin, 41);
+	pool->coinbase[41 + pool->n1_len + 4 + 1 + 8] = 25;
+	cg_memcpy(pool->coinbase + 41 + pool->n1_len + 4 + 1 + 8 + 1, pool->script_pubkey, 25);
+	cg_wunlock(&pool->gbt_lock);
+}
+
+static bool setup_gbt_solo(CURL *curl, struct pool *pool)
+{
+	char s[256];
+	int uninitialised_var(rolltime);
+	bool ret = false;
+	json_t *val = NULL, *res_val, *valid_val;
+
+	if (!opt_btc_address) {
+		if (!opt_decode) {
+			applog(LOG_ERR, "No BTC address specified, unable to mine solo on %s",
+			       pool->rpc_url);
+		}
+		goto out;
+	}
+	if (opt_btc_address[0] != '1')
+	{
+		applog(LOG_ERR, "Bitcoin address must start with 1");
+		goto out;
+	}
+	snprintf(s, 256, "{\"id\": 1, \"method\": \"validateaddress\", \"params\": [\"%s\"]}\n", opt_btc_address);
+	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, true,
+			    false, &rolltime, pool, false);
+	if (!val)
+		goto out;
+	res_val = json_object_get(val, "result");
+	if (!res_val)
+		goto out;
+	valid_val = json_object_get(res_val, "isvalid");
+	if (!valid_val)
+		goto out;
+	if (!json_is_true(valid_val)) {
+		applog(LOG_ERR, "Bitcoin address %s is NOT valid", opt_btc_address);
+		goto out;
+	}
+	applog(LOG_NOTICE, "Solo mining to valid address: %s", opt_btc_address);
+	ret = true;
+	address_to_pubkeyhash(pool->script_pubkey, opt_btc_address);
+	hex2bin(scriptsig_header_bin, scriptsig_header, 41);
+	__setup_gbt_solo(pool);
+
+	if (opt_debug) {
+		char *cb = bin2hex(pool->coinbase, pool->coinbase_len);
+
+		applog(LOG_DEBUG, "Pool %d coinbase %s", pool->pool_no, cb);
+		free(cb);
+	}
+out:
+	if (val)
+		json_decref(val);
+	return ret;
+}
+#else
+static bool setup_gbt_solo(CURL __maybe_unused *curl, struct pool __maybe_unused *pool)
+{
+	return false;
+}
+#endif
+
+static void pool_start_lp(struct pool *pool)
+{
+	if (!pool->lp_started) {
+		pool->lp_started = true;
+		if (unlikely(pthread_create(&pool->longpoll_thread, NULL, longpoll_thread, (void *)pool)))
+			quit(1, "Failed to create pool longpoll thread");
+	}
+}
+
+static bool pool_active(struct pool *pool, bool pinging)
+{
+	struct timeval tv_getwork, tv_getwork_reply;
+	json_t *val = NULL;
+	bool ret = false;
+	CURL *curl;
+	int uninitialised_var(rolltime);
+
+	if (pool->has_gbt)
+		applog(LOG_DEBUG, "Retrieving block template from pool %s", pool->rpc_url);
+	else
+		applog(LOG_INFO, "Testing pool %s", pool->rpc_url);
+
+	/* This is the central point we activate stratum when we can */
+retry_stratum:
+	if (pool->has_stratum) {
+		/* We create the stratum thread for each pool just after
+		 * successful authorisation. Once the init flag has been set
+		 * we never unset it and the stratum thread is responsible for
+		 * setting/unsetting the active flag */
+		bool init = pool_tset(pool, &pool->stratum_init);
+
+		if (!init) {
+#ifdef USE_XTRANONCE
+			bool ret = initiate_stratum(pool) && (!pool->extranonce_subscribe || subscribe_extranonce(pool)) && auth_stratum(pool);
+#else
+			bool ret = initiate_stratum(pool) && auth_stratum(pool);
+#endif
+			if (ret)
+				init_stratum_threads(pool);
+			else
+				pool_tclear(pool, &pool->stratum_init);
+			return ret;
+		}
+		return pool->stratum_active;
+	}
+
+	curl = curl_easy_init();
+	if (unlikely(!curl)) {
+		applog(LOG_ERR, "CURL initialisation failed");
+		return false;
+	}
+
+	/* Probe for GBT support on first pass */
+	if (!pool->probed) {
+		applog(LOG_DEBUG, "Probing for GBT support");
+		val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass,
+					gbt_req, true, false, &rolltime, pool, false);
+		if (val) {
+			json_t *rules_arr = json_object_get(val, "rules");
+
+			if (!gbt_check_rules(rules_arr, gbt_understood_rules)) {
+				applog(LOG_DEBUG, "Not all rules understood for GBT");
+				json_decref(val);
+				val = NULL;
+			}
+		}
+		if (!val) {
+			json_t *rules_arr;
+
+			applog(LOG_DEBUG, "Probing for GBT solo support");
+			val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass,
+					gbt_solo_req, true, false, &rolltime, pool, false);
+			rules_arr = json_object_get(val, "rules");
+			if (!gbt_check_rules(rules_arr, gbt_solo_understood_rules)) {
+				applog(LOG_DEBUG, "Not all rules understood for GBT solo");
+				json_decref(val);
+				val = NULL;
+			}
+		}
+		if (val) {
+			bool append = false, submit = false, transactions = false;
+			json_t *res_val, *mutables;
+			int i, mutsize = 0;
+
+			res_val = json_object_get(val, "result");
+			if (res_val) {
+				mutables = json_object_get(res_val, "mutable");
+				mutsize = json_array_size(mutables);
+			}
+
+			for (i = 0; i < mutsize; i++) {
+				json_t *arrval = json_array_get(mutables, i);
+
+				if (json_is_string(arrval)) {
+					const char *mutable = json_string_value(arrval);
+
+					if (!strncasecmp(mutable, "coinbase/append", 15))
+						append = true;
+					else if (!strncasecmp(mutable, "submit/coinbase", 15))
+						submit = true;
+					else if (!strncasecmp(mutable, "transactions", 12))
+						transactions = true;
+				}
+			}
+			json_decref(val);
+
+			/* Only use GBT if it supports coinbase append and
+			 * submit coinbase */
+			if (append && submit) {
+				pool->has_gbt = true;
+				pool->rpc_req = gbt_req;
+			} else if (transactions) {
+				pool->rpc_req = gbt_solo_req;
+				/* Set up gbt_curl before setting gbt_solo
+				 * flag to prevent longpoll thread from
+				 * trying to use an un'inited gbt_curl */
+				pool->gbt_curl = curl_easy_init();
+				if (unlikely(!pool->gbt_curl))
+					quit(1, "GBT CURL initialisation failed");
+				pool->gbt_solo = true;
+				if (!opt_btcd)
+					opt_btcd = pool;
+			}
+		}
+		/* Reset this so we can probe fully just after this. It will be
+		 * set to true that time.*/
+		pool->probed = false;
+
+		if (pool->has_gbt)
+			applog(LOG_DEBUG, "GBT coinbase + append support found, switching to GBT protocol");
+		else if (pool->gbt_solo)
+			applog(LOG_DEBUG, "GBT coinbase without append found, switching to GBT solo protocol");
+		else
+			applog(LOG_DEBUG, "No GBT coinbase + append support found, pool unusable if it has no stratum");
+	}
+
+	cgtime(&tv_getwork);
+	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass,
+			    pool->rpc_req, true, false, &rolltime, pool, false);
+	cgtime(&tv_getwork_reply);
+
+	/* Detect if a http pool has an X-Stratum header at startup,
+	 * and if so, switch to that in preference to gbt if it works */
+	if (pool->stratum_url && !opt_fix_protocol && stratum_works(pool)) {
+		applog(LOG_NOTICE, "Switching pool %d %s to %s", pool->pool_no, pool->rpc_url, pool->stratum_url);
+		if (!pool->rpc_url)
+			pool->rpc_url = strdup(pool->stratum_url);
+		pool->has_stratum = true;
+		curl_easy_cleanup(curl);
+
+		goto retry_stratum;
+	}
+
+	if (!pool->has_stratum && !pool->gbt_solo && !pool->has_gbt) {
+		applog(LOG_WARNING, "No Stratum, GBT or Solo support in pool %d %s unable to use", pool->pool_no, pool->rpc_url);
+		return false;
+	}
+	if (val) {
+		struct work *work = make_work();
+		bool rc;
+
+		rc = work_decode(pool, work, val);
+		if (rc) {
+			applog(LOG_DEBUG, "Successfully retrieved and deciphered work from pool %u %s",
+			       pool->pool_no, pool->rpc_url);
+			if (pool->gbt_solo) {
+				ret = setup_gbt_solo(curl, pool);
+				if (ret)
+					pool_start_lp(pool);
+				free_work(work);
+				goto out;
+			}
+			work->pool = pool;
+			work->rolltime = rolltime;
+			copy_time(&work->tv_getwork, &tv_getwork);
+			copy_time(&work->tv_getwork_reply, &tv_getwork_reply);
+			work->getwork_mode = GETWORK_MODE_TESTPOOL;
+			calc_diff(work, 0);
+			applog(LOG_DEBUG, "Pushing pooltest work to base pool");
+
+			stage_work(work);
+			total_getworks++;
+			pool->getwork_requested++;
+			ret = true;
+		} else {
+			applog(LOG_DEBUG, "Successfully retrieved but FAILED to decipher work from pool %u %s",
+			       pool->pool_no, pool->rpc_url);
+			free_work(work);
+		}
+
+		if (pool->lp_url)
+			goto out;
+
+		/* Decipher the longpoll URL, if any, and store it in ->lp_url */
+		if (pool->hdr_path) {
+			char *copy_start, *hdr_path;
+			bool need_slash = false;
+			size_t siz;
+
+			hdr_path = pool->hdr_path;
+			if (strstr(hdr_path, "://")) {
+				pool->lp_url = hdr_path;
+				hdr_path = NULL;
+			} else {
+				/* absolute path, on current server */
+				copy_start = (*hdr_path == '/') ? (hdr_path + 1) : hdr_path;
+				if (pool->rpc_url[strlen(pool->rpc_url) - 1] != '/')
+					need_slash = true;
+
+				siz = strlen(pool->rpc_url) + strlen(copy_start) + 2;
+				pool->lp_url = cgmalloc(siz);
+				snprintf(pool->lp_url, siz, "%s%s%s", pool->rpc_url, need_slash ? "/" : "", copy_start);
+			}
+		} else
+			pool->lp_url = NULL;
+
+		pool_start_lp(pool);
+	} else {
+		applog(LOG_DEBUG, "FAILED to retrieve work from pool %u %s",
+		       pool->pool_no, pool->rpc_url);
+		if (!pinging && !pool->idle)
+			applog(LOG_WARNING, "Pool %u slow/down or URL or credentials invalid", pool->pool_no);
+	}
+out:
+	if (val)
+		json_decref(val);
+	curl_easy_cleanup(curl);
+	return ret;
+}
+
+static void pool_resus(struct pool *pool)
+{
+	pool->seq_getfails = 0;
+	if (pool_strategy == POOL_FAILOVER && pool->prio < cp_prio())
+		applog(LOG_WARNING, "Pool %d %s alive, testing stability", pool->pool_no, pool->rpc_url);
+	else
+		applog(LOG_INFO, "Pool %d %s alive", pool->pool_no, pool->rpc_url);
+}
+
+static bool work_filled;
+static bool work_emptied;
+
+/* If this is called non_blocking, it will return NULL for work so that must
+ * be handled. */
+static struct work *hash_pop(bool blocking)
+{
+	struct work *work = NULL, *tmp;
+	int hc;
+
+	mutex_lock(stgd_lock);
+	if (!HASH_COUNT(staged_work)) {
+		work_emptied = true;
+		if (!blocking)
+			goto out_unlock;
+		do {
+			struct timespec abstime, tdiff = {10, 0};
+			int rc;
+
+			cgcond_time(&abstime);
+			timeraddspec(&abstime, &tdiff);
+			pthread_cond_signal(&gws_cond);
+			rc = pthread_cond_timedwait(&getq->cond, stgd_lock, &abstime);
+			/* Check again for !no_work as multiple threads may be
+				* waiting on this condition and another may set the
+				* bool separately. */
+			if (rc && !no_work) {
+				no_work = true;
+				applog(LOG_WARNING, "Waiting for work to be available from pools.");
+			}
+		} while (!HASH_COUNT(staged_work));
+	}
+
+	if (no_work) {
+		applog(LOG_WARNING, "Work available from pools, resuming.");
+		no_work = false;
+	}
+
+	hc = HASH_COUNT(staged_work);
+	/* Find clone work if possible, to allow masters to be reused */
+	if (hc > staged_rollable) {
+		HASH_ITER(hh, staged_work, work, tmp) {
+			if (!work_rollable(work))
+				break;
+		}
+	} else
+		work = staged_work;
+	HASH_DEL(staged_work, work);
+	if (work_rollable(work))
+		staged_rollable--;
+
+	/* Signal the getwork scheduler to look for more work */
+	pthread_cond_signal(&gws_cond);
+
+	/* Signal hash_pop again in case there are mutliple hash_pop waiters */
+	pthread_cond_signal(&getq->cond);
+
+	/* Keep track of last getwork grabbed */
+	last_getwork = time(NULL);
+out_unlock:
+	mutex_unlock(stgd_lock);
+
+	return work;
+}
+
+static void gen_hash(unsigned char *data, unsigned char *hash, int len)
+{
+	unsigned char hash1[32];
+
+	sha256(data, len, hash1);
+	sha256(hash1, 32, hash);
+}
+
+void set_target(unsigned char *dest_target, double diff)
+{
+	unsigned char target[32];
+	uint64_t *data64, h64;
+	double d64, dcut64;
+
+	if (unlikely(diff == 0.0)) {
+		/* This shouldn't happen but best we check to prevent a crash */
+		applog(LOG_ERR, "Diff zero passed to set_target");
+		diff = 1.0;
+	}
+
+	d64 = truediffone;
+	d64 /= diff;
+
+	dcut64 = d64 / bits192;
+	h64 = dcut64;
+	data64 = (uint64_t *)(target + 24);
+	*data64 = htole64(h64);
+	dcut64 = h64;
+	dcut64 *= bits192;
+	d64 -= dcut64;
+
+	dcut64 = d64 / bits128;
+	h64 = dcut64;
+	data64 = (uint64_t *)(target + 16);
+	*data64 = htole64(h64);
+	dcut64 = h64;
+	dcut64 *= bits128;
+	d64 -= dcut64;
+
+	dcut64 = d64 / bits64;
+	h64 = dcut64;
+	data64 = (uint64_t *)(target + 8);
+	*data64 = htole64(h64);
+	dcut64 = h64;
+	dcut64 *= bits64;
+	d64 -= dcut64;
+
+	h64 = d64;
+	data64 = (uint64_t *)(target);
+	*data64 = htole64(h64);
+
+	if (opt_debug) {
+		char *htarget = bin2hex(target, 32);
+
+		applog(LOG_DEBUG, "Generated target %s", htarget);
+		free(htarget);
+	}
+	cg_memcpy(dest_target, target, 32);
+}
+
+#if defined (USE_AVALON2) || defined (USE_AVALON4) || defined (USE_AVALON7) || defined (USE_AVALON8) || defined (USE_AVALON9)  \
+|| defined (USE_AVALON_MINER) || defined (USE_HASHRATIO) || defined (USE_AVALONLC3)
+bool submit_nonce2_nonce(struct thr_info *thr, struct pool *pool, struct pool *real_pool,
+			 uint32_t nonce2, uint32_t nonce,  uint32_t ntime)
+{
+	const int thr_id = thr->id;
+	struct cgpu_info *cgpu = thr->cgpu;
+	struct device_drv *drv = cgpu->drv;
+	struct work *work = make_work();
+	bool ret;
+
+	cg_wlock(&pool->data_lock);
+	pool->nonce2 = nonce2;
+	cg_wunlock(&pool->data_lock);
+
+	gen_stratum_work(pool, work);
+	roll_work_ntime(work, ntime);
+
+	work->pool = real_pool;
+	/* Inherit the sdiff from the original stratum */
+	work->sdiff = pool->sdiff;
+
+	work->thr_id = thr_id;
+	work->work_block = work_block;
+	work->pool->works++;
+
+	work->mined = true;
+	work->device_diff = MIN(drv->max_diff, work->work_difficulty);
+	work->device_diff = MAX(drv->min_diff, work->device_diff);
+
+	ret = submit_nonce(thr, work, nonce);
+	free_work(work);
+	return ret;
+}
+
+uint32_t gen_merkle_root(struct pool *pool, uint64_t nonce2)
+{
+	unsigned char merkle_root[32], merkle_sha[64];
+	uint32_t *data32, *swap32, tail;
+	uint64_t nonce2le;
+	int i;
+
+	/* Update coinbase. Always use an LE encoded nonce2 to fill in values
+	 * from left to right and prevent overflow errors with small n2sizes */
+	nonce2le = htole64(nonce2);
+	cg_memcpy(pool->coinbase + pool->nonce2_offset, &nonce2le, pool->n2size);
+
+	/* Generate merkle root */
+	gen_hash(pool->coinbase, merkle_root, pool->coinbase_len);
+	cg_memcpy(merkle_sha, merkle_root, 32);
+	for (i = 0; i < pool->merkles; i++) {
+		cg_memcpy(merkle_sha + 32, pool->swork.merkle_bin[i], 32);
+		gen_hash(merkle_sha, merkle_root, 64);
+		cg_memcpy(merkle_sha, merkle_root, 32);
+	}
+	data32 = (uint32_t *)merkle_sha;
+	swap32 = (uint32_t *)merkle_root;
+	flip32(swap32, data32);
+
+	{
+		char *merkle_hash;
+
+		merkle_hash = bin2hex((const unsigned char *)merkle_root, 32);
+		applog(LOG_DEBUG, "[M-N2]: %s-%08x-%08x", merkle_hash, (uint32_t)nonce2le, (uint32_t)nonce2);
+		free(merkle_hash);
+	}
+
+	cg_memcpy(&tail, merkle_root + 28, 4);
+
+	return tail;
+}
+#endif
+
+#ifdef USE_BITMAIN_SOC
+void get_work_by_nonce2(struct thr_info *thr,
+						struct work **work,
+						struct pool *pool,
+						struct pool *real_pool,
+						uint64_t nonce2,
+						uint32_t version)
+{
+	*work = make_work();
+	const int thr_id = thr->id;
+	struct cgpu_info *cgpu = thr->cgpu;
+	struct device_drv *drv = cgpu->drv;
+	cg_wlock(&pool->data_lock);
+	pool->nonce2 = nonce2;
+	//if(pool->support_vil) // comment as default
+	version = Swap32(version);
+	cg_memcpy(pool->header_bin, &version, 4);
+	cg_wunlock(&pool->data_lock);
+
+	gen_stratum_work(pool, *work);
+
+	(*work)->pool = real_pool;
+
+	(*work)->thr_id = thr_id;
+	(*work)->work_block = work_block;
+	(*work)->pool->works++;
+
+	(*work)->mined = true;
+	(*work)->version = version;
+}
+#endif
+
+#if STRATUM_WORK_TIMING
+cglock_t swt_lock;
+uint64_t stratum_work_count;
+uint64_t stratum_work_time;
+uint64_t stratum_work_min;
+uint64_t stratum_work_max;
+uint64_t stratum_work_time0;
+uint64_t stratum_work_time10;
+uint64_t stratum_work_time100;
+#endif
+
+/* Generates stratum based work based on the most recent notify information
+ * from the pool. This will keep generating work while a pool is down so we use
+ * other means to detect when the pool has died in stratum_thread */
+static void gen_stratum_work(struct pool *pool, struct work *work)
+{
+	unsigned char merkle_root[32], merkle_sha[64];
+	uint32_t *data32, *swap32;
+#if STRATUM_WORK_TIMING
+	struct timeval stt;
+	double usec;
+#endif
+	uint64_t nonce2le;
+	int i;
+
+#if STRATUM_WORK_TIMING
+	cgtime(&stt);
+#endif
+
+	cg_wlock(&pool->data_lock);
+
+	/* Update coinbase. Always use an LE encoded nonce2 to fill in values
+	 * from left to right and prevent overflow errors with small n2sizes */
+	nonce2le = htole64(pool->nonce2);
+	cg_memcpy(pool->coinbase + pool->nonce2_offset, &nonce2le, pool->n2size);
+	work->nonce2 = pool->nonce2++;
+	work->nonce2_len = pool->n2size;
+
+	/* Downgrade to a read lock to read off the pool variables */
+	cg_dwlock(&pool->data_lock);
+
+	/* Generate merkle root */
+	gen_hash(pool->coinbase, merkle_root, pool->coinbase_len);
+	cg_memcpy(merkle_sha, merkle_root, 32);
+	for (i = 0; i < pool->merkles; i++) {
+		cg_memcpy(merkle_sha + 32, pool->swork.merkle_bin[i], 32);
+		gen_hash(merkle_sha, merkle_root, 64);
+		cg_memcpy(merkle_sha, merkle_root, 32);
+	}
+	data32 = (uint32_t *)merkle_sha;
+	swap32 = (uint32_t *)merkle_root;
+	flip32(swap32, data32);
+
+	/* Copy the data template from header_bin */
+	cg_memcpy(work->data, pool->header_bin, 112);
+	cg_memcpy(work->data + 36, merkle_root, 32);
+
+	/* Store the stratum work diff to check it still matches the pool's
+	 * stratum diff when submitting shares */
+	work->sdiff = pool->sdiff;
+
+	/* Copy parameters required for share submission */
+	work->job_id = strdup(pool->swork.job_id);
+	work->nonce1 = strdup(pool->nonce1);
+	work->ntime = strdup(pool->ntime);
+	cg_runlock(&pool->data_lock);
+
+	if (opt_debug) {
+		char *header, *merkle_hash;
+
+		header = bin2hex(work->data, 112);
+		merkle_hash = bin2hex((const unsigned char *)merkle_root, 32);
+		applog(LOG_DEBUG, "Generated stratum merkle %s", merkle_hash);
+		applog(LOG_DEBUG, "Generated stratum header %s", header);
+		applog(LOG_DEBUG, "Work job_id %s nonce2 %"PRIu64" ntime %s", work->job_id,
+		       work->nonce2, work->ntime);
+		free(header);
+		free(merkle_hash);
+	}
+
+	calc_midstate(pool, work);
+	set_target(work->target, work->sdiff);
+
+	local_work++;
+	work->pool = pool;
+	work->stratum = true;
+	work->nonce = 0;
+	work->longpoll = false;
+	work->getwork_mode = GETWORK_MODE_STRATUM;
+	work->work_block = work_block;
+	/* Nominally allow a driver to ntime roll 60 seconds */
+	work->drv_rolllimit = 60;
+	calc_diff(work, work->sdiff);
+
+	cgtime(&work->tv_staged);
+
+#if STRATUM_WORK_TIMING
+	usec = us_tdiff(&work->tv_staged, &stt);
+	cg_wlock(&swt_lock);
+	stratum_work_count++;
+	stratum_work_time += usec;
+	if (stratum_work_min == 0 || stratum_work_min > usec)
+		stratum_work_min = usec;
+	if (stratum_work_max < usec)
+		stratum_work_max = usec;
+	if (usec == 0)
+		stratum_work_time0++;
+	else
+	{
+		if (usec >= 10)
+		{
+			stratum_work_time10++;
+			if (usec >= 100)
+				stratum_work_time100++;
+		}
+	}
+	cg_wunlock(&swt_lock);
+#endif
+}
+
+#ifdef HAVE_LIBCURL
+static void gen_solo_work(struct pool *pool, struct work *work);
+
+/* Use the one instance of gbt_curl, protecting the bool with the gbt_lock but
+ * avoiding holding the lock once we've set the bool. */
+static void get_gbt_curl(struct pool *pool, int poll)
+{
+	cg_ilock(&pool->gbt_lock);
+	while (pool->gbt_curl_inuse) {
+		cg_uilock(&pool->gbt_lock);
+		cgsleep_ms(poll);
+		cg_ilock(&pool->gbt_lock);
+	}
+	cg_ulock(&pool->gbt_lock);
+	pool->gbt_curl_inuse = true;
+	cg_wunlock(&pool->gbt_lock);
+}
+
+/* No need for locking here */
+static inline void release_gbt_curl(struct pool *pool)
+{
+	pool->gbt_curl_inuse = false;
+}
+
+static void update_gbt_solo(struct pool *pool)
+{
+	struct work *work = make_work();
+	int rolltime;
+	json_t *val;
+
+	get_gbt_curl(pool, 10);
+retry:
+	/* Bitcoind doesn't like many open RPC connections. */
+	curl_easy_setopt(pool->gbt_curl, CURLOPT_FORBID_REUSE, 1);
+	val = json_rpc_call(pool->gbt_curl, pool->rpc_url, pool->rpc_userpass, pool->rpc_req,
+			    true, false, &rolltime, pool, false);
+
+	if (likely(val)) {
+		bool rc = work_decode(pool, work, val);
+
+		if (rc) {
+			__setup_gbt_solo(pool);
+			gen_solo_work(pool, work);
+			stage_work(work);
+		} else
+			free_work(work);
+		json_decref(val);
+	} else {
+		applog(LOG_DEBUG, "Pool %d json_rpc_call failed on get gbt, retrying in 5s",
+		       pool->pool_no);
+		if (++pool->seq_getfails > 5) {
+			pool_died(pool);
+			goto out;
+		}
+		cgsleep_ms(5000);
+		goto retry;
+	}
+out:
+	release_gbt_curl(pool);
+}
+
+static void gen_solo_work(struct pool *pool, struct work *work)
+{
+	unsigned char merkle_root[32], merkle_sha[64];
+	uint32_t *data32, *swap32;
+	struct timeval now;
+	uint64_t nonce2le;
+	int i;
+
+	cgtime(&now);
+	if (now.tv_sec - pool->tv_lastwork.tv_sec > 60)
+		update_gbt_solo(pool);
+
+	cg_wlock(&pool->gbt_lock);
+
+	/* Update coinbase. Always use an LE encoded nonce2 to fill in values
+	 * from left to right and prevent overflow errors with small n2sizes */
+	nonce2le = htole64(pool->nonce2);
+	cg_memcpy(pool->coinbase + pool->nonce2_offset, &nonce2le, pool->n2size);
+	work->nonce2 = pool->nonce2++;
+	work->nonce2_len = pool->n2size;
+	work->gbt_txns = pool->transactions + 1;
+
+	/* Downgrade to a read lock to read off the pool variables */
+	cg_dwlock(&pool->gbt_lock);
+	work->coinbase = bin2hex(pool->coinbase, pool->coinbase_len);
+	/* Generate merkle root */
+	gen_hash(pool->coinbase, merkle_root, pool->coinbase_len);
+	cg_memcpy(merkle_sha, merkle_root, 32);
+	for (i = 0; i < pool->merkles; i++) {
+		unsigned char *merkle_bin;
+
+		merkle_bin = pool->merklebin + (i * 32);
+		cg_memcpy(merkle_sha + 32, merkle_bin, 32);
+		gen_hash(merkle_sha, merkle_root, 64);
+		cg_memcpy(merkle_sha, merkle_root, 32);
+	}
+	data32 = (uint32_t *)merkle_sha;
+	swap32 = (uint32_t *)merkle_root;
+	flip32(swap32, data32);
+
+	/* Copy the data template from header_bin */
+	cg_memcpy(work->data, pool->header_bin, 112);
+	cg_memcpy(work->data + 36, merkle_root, 32);
+
+	work->sdiff = pool->sdiff;
+
+	/* Copy parameters required for share submission */
+	work->ntime = strdup(pool->ntime);
+	cg_memcpy(work->target, pool->gbt_target, 32);
+	cg_runlock(&pool->gbt_lock);
+
+	if (opt_debug) {
+		char *header, *merkle_hash;
+
+		header = bin2hex(work->data, 112);
+		merkle_hash = bin2hex((const unsigned char *)merkle_root, 32);
+		applog(LOG_DEBUG, "Generated GBT solo merkle %s", merkle_hash);
+		applog(LOG_DEBUG, "Generated GBT solo header %s", header);
+		applog(LOG_DEBUG, "Work nonce2 %"PRIu64" ntime %s", work->nonce2,
+		       work->ntime);
+		free(header);
+		free(merkle_hash);
+	}
+
+	calc_midstate(pool, work);
+
+	local_work++;
+	work->gbt = true;
+	work->pool = pool;
+	work->nonce = 0;
+	work->longpoll = false;
+	work->getwork_mode = GETWORK_MODE_SOLO;
+	work->work_block = work_block;
+	/* Nominally allow a driver to ntime roll 60 seconds */
+	work->drv_rolllimit = 60;
+	calc_diff(work, work->sdiff);
+
+	cgtime(&work->tv_staged);
+}
+#endif
+
+/* The time difference in seconds between when this device last got work via
+ * get_work() and generated a valid share. */
+int share_work_tdiff(struct cgpu_info *cgpu)
+{
+	return last_getwork - cgpu->last_device_valid_work;
+}
+
+static void set_benchmark_work(struct cgpu_info *cgpu, struct work *work)
+{
+	cgpu->lodiff += cgpu->direction;
+	if (cgpu->lodiff < 1)
+		cgpu->direction = 1;
+	if (cgpu->lodiff > 15) {
+		cgpu->direction = -1;
+		if (++cgpu->hidiff > 15)
+			cgpu->hidiff = 0;
+		cg_memcpy(work, &bench_hidiff_bins[cgpu->hidiff][0], 160);
+	} else
+		cg_memcpy(work, &bench_lodiff_bins[cgpu->lodiff][0], 160);
+}
+
+struct work *get_work(struct thr_info *thr, const int thr_id)
+{
+	struct cgpu_info *cgpu = thr->cgpu;
+	struct work *work = NULL;
+	time_t diff_t;
+
+	thread_reportout(thr);
+	applog(LOG_DEBUG, "Popping work from get queue to get work");
+	diff_t = time(NULL);
+	while (!work) {
+		work = hash_pop(true);
+		if (stale_work(work, false)) {
+			discard_work(work);
+			wake_gws();
+		}
+	}
+	diff_t = time(NULL) - diff_t;
+	/* Since this is a blocking function, we need to add grace time to
+	 * the device's last valid work to not make outages appear to be
+	 * device failures. */
+	if (diff_t > 0) {
+		applog(LOG_DEBUG, "Get work blocked for %d seconds", (int)diff_t);
+		cgpu->last_device_valid_work += diff_t;
+	}
+	applog(LOG_DEBUG, "Got work from get queue to get work for thread %d", thr_id);
+
+	work->thr_id = thr_id;
+	if (opt_benchmark)
+		set_benchmark_work(cgpu, work);
+
+	thread_reportin(thr);
+	work->mined = true;
+	work->device_diff = MIN(cgpu->drv->max_diff, work->work_difficulty);
+	work->device_diff = MAX(cgpu->drv->min_diff, work->device_diff);
+	return work;
+}
+
+/* Submit a copy of the tested, statistic recorded work item asynchronously */
+static void submit_work_async(struct work *work)
+{
+	struct pool *pool = work->pool;
+	pthread_t submit_thread;
+
+	cgtime(&work->tv_work_found);
+	if (opt_benchmark) {
+		struct cgpu_info *cgpu = get_thr_cgpu(work->thr_id);
+
+		mutex_lock(&stats_lock);
+		cgpu->accepted++;
+		total_accepted++;
+		pool->accepted++;
+		cgpu->diff_accepted += work->work_difficulty;
+		total_diff_accepted += work->work_difficulty;
+		pool->diff_accepted += work->work_difficulty;
+		mutex_unlock(&stats_lock);
+
+		applog(LOG_NOTICE, "Accepted %s %d benchmark share nonce %08x",
+		       cgpu->drv->name, cgpu->device_id, *(uint32_t *)(work->data + 64 + 12));
+		return;
+	}
+
+	if (stale_work(work, true)) {
+		if (opt_submit_stale)
+			applog(LOG_NOTICE, "Pool %d stale share detected, submitting as user requested", pool->pool_no);
+		else if (pool->submit_old)
+			applog(LOG_NOTICE, "Pool %d stale share detected, submitting as pool requested", pool->pool_no);
+		else {
+			applog(LOG_NOTICE, "Pool %d stale share detected, discarding", pool->pool_no);
+			sharelog("discard", work);
+
+			mutex_lock(&stats_lock);
+			total_stale++;
+			pool->stale_shares++;
+			total_diff_stale += work->work_difficulty;
+			pool->diff_stale += work->work_difficulty;
+			mutex_unlock(&stats_lock);
+
+			free_work(work);
+			return;
+		}
+		work->stale = true;
+	}
+
+	if (work->stratum) {
+		applog(LOG_DEBUG, "Pushing pool %d work to stratum queue", pool->pool_no);
+		if (unlikely(!pool->stratum_q || !tq_push(pool->stratum_q, work))) {
+			applog(LOG_DEBUG, "Discarding work from removed pool");
+			free_work(work);
+		}
+	} else {
+		applog(LOG_DEBUG, "Pushing submit work to work thread");
+		if (unlikely(pthread_create(&submit_thread, NULL, submit_work_thread, (void *)work)))
+			quit(1, "Failed to create submit_work_thread");
+	}
+}
+
+void inc_hw_errors_n(struct thr_info *thr, int n)
+{
+	applog(LOG_INFO, "%s %d: invalid nonce - HW error", thr->cgpu->drv->name,
+	       thr->cgpu->device_id);
+
+	mutex_lock(&stats_lock);
+	hw_errors += n;
+	thr->cgpu->hw_errors += n;
+	mutex_unlock(&stats_lock);
+
+	thr->cgpu->drv->hw_error(thr);
+}
+
+void inc_hw_errors(struct thr_info *thr)
+{
+	inc_hw_errors_n(thr, 1);
+}
+
+/* Fills in the work nonce and builds the output data in work->hash */
+static void rebuild_nonce(struct work *work, uint32_t nonce)
+{
+	uint32_t *work_nonce = (uint32_t *)(work->data + 64 + 12);
+
+	*work_nonce = htole32(nonce);
+
+	regen_hash(work);
+}
+
+/* For testing a nonce against diff 1 */
+bool test_nonce(struct work *work, uint32_t nonce)
+{
+	uint32_t *hash_32 = (uint32_t *)(work->hash + 28);
+
+	rebuild_nonce(work, nonce);
+	return (*hash_32 == 0);
+}
+
+/* For testing a nonce against an arbitrary diff */
+bool test_nonce_diff(struct work *work, uint32_t nonce, double diff)
+{
+	uint64_t *hash64 = (uint64_t *)(work->hash + 24), diff64;
+
+	rebuild_nonce(work, nonce);
+	diff64 = 0x00000000ffff0000ULL;
+	diff64 /= diff;
+
+	return (le64toh(*hash64) <= diff64);
+}
+
+/* testing a nonce and return the diff - 0 means invalid */
+double test_nonce_value(struct work *work, uint32_t nonce)
+{
+	uint32_t *hash_32 = (uint32_t *)(work->hash + 28);
+	double d64, s64, ds;
+
+	rebuild_nonce(work, nonce);
+	if (*hash_32 != 0)
+		return 0.0;
+
+	d64 = truediffone;
+	s64 = le256todouble(work->hash);
+	ds = d64 / s64;
+
+	return ds;
+}
+
+static void update_work_stats(struct thr_info *thr, struct work *work)
+{
+	double test_diff = current_diff;
+
+	work->share_diff = share_diff(work);
+
+	if (unlikely(work->share_diff >= test_diff)) {
+		work->block = true;
+		work->pool->solved++;
+		found_blocks++;
+		work->mandatory = true;
+		applog(LOG_NOTICE, "Found block for pool %d!", work->pool->pool_no);
+	}
+
+	mutex_lock(&stats_lock);
+	total_diff1 += work->device_diff;
+	thr->cgpu->diff1 += work->device_diff;
+	work->pool->diff1 += work->device_diff;
+	thr->cgpu->last_device_valid_work = time(NULL);
+	mutex_unlock(&stats_lock);
+}
+
+/* To be used once the work has been tested to be meet diff1 and has had its
+ * nonce adjusted. Returns true if the work target is met. */
+bool submit_tested_work(struct thr_info *thr, struct work *work)
+{
+	struct work *work_out;
+	update_work_stats(thr, work);
+
+	// dev testing logging the difficulty of all nonces
+	//double diff = truediffone / le256todouble(work->hash);
+	//applog(LOG_ERR, "%s() %s %d: diff=%.1f", __func__,
+	//	thr->cgpu->drv->name, thr->cgpu->device_id, diff);
+
+	if (!fulltest(work->hash, work->target)) {
+		applog(LOG_INFO, "%s %d: Share above target", thr->cgpu->drv->name,
+		       thr->cgpu->device_id);
+		return false;
+	}
+	work_out = copy_work(work);
+	submit_work_async(work_out);
+	return true;
+}
+
+/* Rudimentary test to see if cgpu has returned the same nonce twice in a row which is
+ * always going to be a duplicate which should be reported as a hw error. */
+static bool new_nonce(struct thr_info *thr, uint32_t nonce)
+{
+	struct cgpu_info *cgpu = thr->cgpu;
+
+	if (unlikely(cgpu->last_nonce == nonce)) {
+		applog(LOG_INFO, "%s %d duplicate share detected as HW error",
+		       cgpu->drv->name, cgpu->device_id);
+		return false;
+	}
+	cgpu->last_nonce = nonce;
+	return true;
+}
+
+/* Returns true if nonce for work was a valid share and not a dupe of the very last
+ * nonce submitted by this device. */
+bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce)
+{
+	if (new_nonce(thr, nonce) && test_nonce(work, nonce))
+		submit_tested_work(thr, work);
+	else {
+		inc_hw_errors(thr);
+		return false;
+	}
+
+	if (opt_benchfile && opt_benchfile_display)
+		benchfile_dspwork(work, nonce);
+
+	return true;
+}
+
+/* Allows drivers to submit work items where the driver has changed the ntime
+ * value by noffset. Must be only used with a work protocol that does not ntime
+ * roll itself intrinsically to generate work (eg stratum). We do not touch
+ * the original work struct, but the copy of it only. */
+bool submit_noffset_nonce(struct thr_info *thr, struct work *work_in, uint32_t nonce,
+			  int noffset)
+{
+	struct work *work = make_work();
+	bool ret = false;
+
+	_copy_work(work, work_in, noffset);
+	if (!test_nonce(work, nonce)) {
+		free_work(work);
+		inc_hw_errors(thr);
+		goto out;
+	}
+	update_work_stats(thr, work);
+
+	if (opt_benchfile && opt_benchfile_display)
+		benchfile_dspwork(work, nonce);
+
+	ret = true;
+	if (!fulltest(work->hash, work->target)) {
+		free_work(work);
+		applog(LOG_INFO, "%s %d: Share above target", thr->cgpu->drv->name,
+		       thr->cgpu->device_id);
+		goto  out;
+	}
+	submit_work_async(work);
+
+out:
+	return ret;
+}
+
+static inline bool abandon_work(struct work *work, struct timeval *wdiff, uint64_t hashes)
+{
+	if (wdiff->tv_sec > max_scantime || hashes >= 0xfffffffe ||
+	    stale_work(work, false))
+		return true;
+	return false;
+}
+
+static void mt_disable(struct thr_info *mythr, const int thr_id,
+		       struct device_drv *drv)
+{
+	applog(LOG_WARNING, "Thread %d being disabled", thr_id);
+	mythr->cgpu->rolling = 0;
+	applog(LOG_DEBUG, "Waiting on sem in miner thread");
+	cgsem_wait(&mythr->sem);
+	applog(LOG_WARNING, "Thread %d being re-enabled", thr_id);
+	drv->thread_enable(mythr);
+}
+
+/* The main hashing loop for devices that are slow enough to work on one work
+ * item at a time, without a queue, aborting work before the entire nonce
+ * range has been hashed if needed. */
+static void hash_sole_work(struct thr_info *mythr)
+{
+	const int thr_id = mythr->id;
+	struct cgpu_info *cgpu = mythr->cgpu;
+	struct device_drv *drv = cgpu->drv;
+	struct timeval getwork_start, tv_start, *tv_end, tv_workstart, tv_lastupdate;
+	struct cgminer_stats *dev_stats = &(cgpu->cgminer_stats);
+	struct cgminer_stats *pool_stats;
+	/* Try to cycle approximately 5 times before each log update */
+	const long cycle = opt_log_interval / 5 ? : 1;
+	const bool primary = (!mythr->device_thread) || mythr->primary_thread;
+	struct timeval diff, sdiff, wdiff = {0, 0};
+	uint32_t max_nonce = drv->can_limit_work(mythr);
+	int64_t hashes_done = 0;
+
+	tv_end = &getwork_start;
+	cgtime(&getwork_start);
+	sdiff.tv_sec = sdiff.tv_usec = 0;
+	cgtime(&tv_lastupdate);
+
+	while (likely(!cgpu->shutdown)) {
+		struct work *work = get_work(mythr, thr_id);
+		int64_t hashes;
+
+		mythr->work_restart = false;
+		cgpu->new_work = true;
+
+		cgtime(&tv_workstart);
+		work->nonce = 0;
+		cgpu->max_hashes = 0;
+		if (!drv->prepare_work(mythr, work)) {
+			applog(LOG_ERR, "work prepare failed, exiting "
+				"mining thread %d", thr_id);
+			break;
+		}
+		work->device_diff = MIN(drv->max_diff, work->work_difficulty);
+		work->device_diff = MAX(drv->min_diff, work->device_diff);
+
+		do {
+			cgtime(&tv_start);
+
+			subtime(&tv_start, &getwork_start);
+
+			addtime(&getwork_start, &dev_stats->getwork_wait);
+			if (time_more(&getwork_start, &dev_stats->getwork_wait_max))
+				copy_time(&dev_stats->getwork_wait_max, &getwork_start);
+			if (time_less(&getwork_start, &dev_stats->getwork_wait_min))
+				copy_time(&dev_stats->getwork_wait_min, &getwork_start);
+			dev_stats->getwork_calls++;
+
+			pool_stats = &(work->pool->cgminer_stats);
+
+			addtime(&getwork_start, &pool_stats->getwork_wait);
+			if (time_more(&getwork_start, &pool_stats->getwork_wait_max))
+				copy_time(&pool_stats->getwork_wait_max, &getwork_start);
+			if (time_less(&getwork_start, &pool_stats->getwork_wait_min))
+				copy_time(&pool_stats->getwork_wait_min, &getwork_start);
+			pool_stats->getwork_calls++;
+
+			cgtime(&(work->tv_work_start));
+
+			/* Only allow the mining thread to be cancelled when
+			 * it is not in the driver code. */
+			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+
+			thread_reportin(mythr);
+			hashes = drv->scanhash(mythr, work, work->nonce + max_nonce);
+			thread_reportout(mythr);
+
+			pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+			pthread_testcancel();
+
+			/* tv_end is == &getwork_start */
+			cgtime(&getwork_start);
+
+			if (unlikely(hashes == -1)) {
+				applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id);
+				cgpu->deven = DEV_DISABLED;
+				dev_error(cgpu, REASON_THREAD_ZERO_HASH);
+				cgpu->shutdown = true;
+				break;
+			}
+
+			hashes_done += hashes;
+			if (hashes > cgpu->max_hashes)
+				cgpu->max_hashes = hashes;
+
+			timersub(tv_end, &tv_start, &diff);
+			sdiff.tv_sec += diff.tv_sec;
+			sdiff.tv_usec += diff.tv_usec;
+			if (sdiff.tv_usec > 1000000) {
+				++sdiff.tv_sec;
+				sdiff.tv_usec -= 1000000;
+			}
+
+			timersub(tv_end, &tv_workstart, &wdiff);
+
+			if (unlikely((long)sdiff.tv_sec < cycle)) {
+				int mult;
+
+				if (likely(max_nonce == 0xffffffff))
+					continue;
+
+				mult = 1000000 / ((sdiff.tv_usec + 0x400) / 0x400) + 0x10;
+				mult *= cycle;
+				if (max_nonce > (0xffffffff * 0x400) / mult)
+					max_nonce = 0xffffffff;
+				else
+					max_nonce = (max_nonce * mult) / 0x400;
+			} else if (unlikely(sdiff.tv_sec > cycle))
+				max_nonce = max_nonce * cycle / sdiff.tv_sec;
+			else if (unlikely(sdiff.tv_usec > 100000))
+				max_nonce = max_nonce * 0x400 / (((cycle * 1000000) + sdiff.tv_usec) / (cycle * 1000000 / 0x400));
+
+			timersub(tv_end, &tv_lastupdate, &diff);
+			/* Update the hashmeter at most 5 times per second */
+			if ((hashes_done && (diff.tv_sec > 0 || diff.tv_usec > 200000)) ||
+			    diff.tv_sec >= opt_log_interval) {
+				hashmeter(thr_id, hashes_done);
+				hashes_done = 0;
+				copy_time(&tv_lastupdate, tv_end);
+			}
+
+			if (unlikely(mythr->work_restart)) {
+				/* Apart from device_thread 0, we stagger the
+				 * starting of every next thread to try and get
+				 * all devices busy before worrying about
+				 * getting work for their extra threads */
+				if (!primary) {
+					struct timespec rgtp;
+
+					rgtp.tv_sec = 0;
+					rgtp.tv_nsec = 250 * mythr->device_thread * 1000000;
+					nanosleep(&rgtp, NULL);
+				}
+				break;
+			}
+
+			if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED))
+				mt_disable(mythr, thr_id, drv);
+
+			sdiff.tv_sec = sdiff.tv_usec = 0;
+		} while (!abandon_work(work, &wdiff, cgpu->max_hashes));
+		free_work(work);
+	}
+	cgpu->deven = DEV_DISABLED;
+}
+
+/* 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)
+{
+	do {
+		bool need_work;
+
+		/* 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, thr_id);
+
+			wr_lock(&cgpu->qlock);
+			/* 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
+		 * does have a queue. */
+	} while (!drv->queue_full(cgpu));
+}
+
+/* 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);
+}
+
+struct work *__get_queued(struct cgpu_info *cgpu)
+{
+	struct work *work = NULL;
+
+	if (cgpu->unqueued_work) {
+		work = cgpu->unqueued_work;
+		if (unlikely(stale_work(work, false))) {
+			discard_work(work);
+		} else
+			__add_queued(cgpu, work);
+		cgpu->unqueued_work = NULL;
+		wake_gws();
+	}
+
+	return 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;
+
+	wr_lock(&cgpu->qlock);
+	work = __get_queued(cgpu);
+	wr_unlock(&cgpu->qlock);
+
+	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, thr_id);
+
+	add_queued(cgpu, work);
+	return work;
+}
+
+/* This function is for finding an already queued work item in the
+ * given que hashtable. Code using this function must be able
+ * to handle NULL as a return which implies there is no matching work.
+ * The calling function must lock access to the que if it is required.
+ * The common values for midstatelen, offset, datalen are 32, 64, 12 */
+struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen)
+{
+	struct work *work, *tmp, *ret = NULL;
+
+	HASH_ITER(hh, que, work, tmp) {
+		if (memcmp(work->midstate, midstate, midstatelen) == 0 &&
+		    memcmp(work->data + offset, data, datalen) == 0) {
+			ret = work;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/* This function is for finding an already queued work item in the
+ * device's queued_work hashtable. Code using this function must be able
+ * to handle NULL as a return which implies there is no matching work.
+ * The common values for midstatelen, offset, datalen are 32, 64, 12 */
+struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen)
+{
+	struct work *ret;
+
+	rd_lock(&cgpu->qlock);
+	ret = __find_work_bymidstate(cgpu->queued_work, midstate, midstatelen, data, offset, datalen);
+	rd_unlock(&cgpu->qlock);
+
+	return ret;
+}
+
+struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen)
+{
+	struct work *work, *ret = NULL;
+
+	rd_lock(&cgpu->qlock);
+	work = __find_work_bymidstate(cgpu->queued_work, midstate, midstatelen, data, offset, datalen);
+	if (work)
+		ret = copy_work(work);
+	rd_unlock(&cgpu->qlock);
+
+	return ret;
+}
+
+/* This function is for finding an already queued work item in the
+ * given que hashtable. Code using this function must be able
+ * to handle NULL as a return which implies there is no matching work.
+ * The calling function must lock access to the que if it is required. */
+struct work *__find_work_byid(struct work *queue, uint32_t id)
+{
+	struct work *ret = NULL;
+	HASH_FIND_INT(queue, &id, ret);
+	return ret;
+}
+
+struct work *find_queued_work_byid(struct cgpu_info *cgpu, uint32_t id)
+{
+	struct work *ret;
+
+	rd_lock(&cgpu->qlock);
+	ret = __find_work_byid(cgpu->queued_work, id);
+	rd_unlock(&cgpu->qlock);
+
+	return ret;
+}
+
+struct work *clone_queued_work_byid(struct cgpu_info *cgpu, uint32_t id)
+{
+	struct work *work, *ret = NULL;
+
+	rd_lock(&cgpu->qlock);
+	work = __find_work_byid(cgpu->queued_work, id);
+	if (work)
+		ret = copy_work(work);
+	rd_unlock(&cgpu->qlock);
+
+	return ret;
+}
+
+void __work_completed(struct cgpu_info *cgpu, struct work *work)
+{
+	cgpu->queued_count--;
+	HASH_DEL(cgpu->queued_work, work);
+}
+
+/* This iterates over a queued hashlist finding work started more than secs
+ * seconds ago and discards the work as completed. The driver must set the
+ * work->tv_work_start value appropriately. Returns the number of items aged. */
+int age_queued_work(struct cgpu_info *cgpu, double secs)
+{
+	struct work *work, *tmp;
+	struct timeval tv_now;
+	int aged = 0;
+
+	cgtime(&tv_now);
+
+	wr_lock(&cgpu->qlock);
+	HASH_ITER(hh, cgpu->queued_work, work, tmp) {
+		if (tdiff(&tv_now, &work->tv_work_start) > secs) {
+			__work_completed(cgpu, work);
+			free_work(work);
+			aged++;
+		}
+	}
+	wr_unlock(&cgpu->qlock);
+
+	return aged;
+}
+
+/* This function should be used by queued device drivers when they're sure
+ * the work struct is no longer in use. */
+void work_completed(struct cgpu_info *cgpu, struct work *work)
+{
+	wr_lock(&cgpu->qlock);
+	__work_completed(cgpu, work);
+	wr_unlock(&cgpu->qlock);
+
+	free_work(work);
+}
+
+/* Combines find_queued_work_bymidstate and work_completed in one function
+ * withOUT destroying the work so the driver must free it. */
+struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen)
+{
+	struct work *work;
+
+	wr_lock(&cgpu->qlock);
+	work = __find_work_bymidstate(cgpu->queued_work, midstate, midstatelen, data, offset, datalen);
+	if (work)
+		__work_completed(cgpu, work);
+	wr_unlock(&cgpu->qlock);
+
+	return work;
+}
+
+void flush_queue(struct cgpu_info *cgpu)
+{
+	struct work *work = NULL;
+
+	if (unlikely(!cgpu))
+		return;
+
+	/* Use only a trylock in case we get into a deadlock with a queueing
+	 * function holding the read lock when we're called. */
+	if (wr_trylock(&cgpu->qlock))
+		return;
+	work = cgpu->unqueued_work;
+	cgpu->unqueued_work = NULL;
+	wr_unlock(&cgpu->qlock);
+
+	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
+ * perform a full nonce range and need a queue to maintain the device busy.
+ * Work creation and destruction is not done from within this function
+ * directly. */
+void hash_queued_work(struct thr_info *mythr)
+{
+	struct timeval tv_start = {0, 0}, tv_end;
+	struct cgpu_info *cgpu = mythr->cgpu;
+	struct device_drv *drv = cgpu->drv;
+	const int thr_id = mythr->id;
+	int64_t hashes_done = 0;
+
+	while (likely(!cgpu->shutdown)) {
+		struct timeval diff;
+		int64_t hashes;
+
+		fill_queue(mythr, cgpu, drv, thr_id);
+
+		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;
+			dev_error(cgpu, REASON_THREAD_ZERO_HASH);
+			break;
+		}
+
+		hashes_done += hashes;
+		cgtime(&tv_end);
+		timersub(&tv_end, &tv_start, &diff);
+		/* Update the hashmeter at most 5 times per second */
+		if ((hashes_done && (diff.tv_sec > 0 || diff.tv_usec > 200000)) ||
+		    diff.tv_sec >= opt_log_interval) {
+			hashmeter(thr_id, hashes_done);
+			hashes_done = 0;
+			copy_time(&tv_start, &tv_end);
+		}
+
+		if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED))
+			mt_disable(mythr, thr_id, drv);
+
+		if (mythr->work_update) {
+			drv->update_work(cgpu);
+			mythr->work_update = false;
+		}
+	}
+	cgpu->deven = DEV_DISABLED;
+}
+
+/* This version of hash_work is for devices drivers that want to do their own
+ * work management entirely, usually by using get_work(). Note that get_work
+ * is a blocking function and will wait indefinitely if no work is available
+ * so this must be taken into consideration in the driver. */
+void hash_driver_work(struct thr_info *mythr)
+{
+	struct timeval tv_start = {0, 0}, tv_end;
+	struct cgpu_info *cgpu = mythr->cgpu;
+	struct device_drv *drv = cgpu->drv;
+	const int thr_id = mythr->id;
+	int64_t hashes_done = 0;
+
+	while (likely(!cgpu->shutdown)) {
+		struct timeval diff;
+		int64_t hashes;
+
+		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;
+			dev_error(cgpu, REASON_THREAD_ZERO_HASH);
+			break;
+		}
+
+		hashes_done += hashes;
+		cgtime(&tv_end);
+		timersub(&tv_end, &tv_start, &diff);
+		/* Update the hashmeter at most 5 times per second */
+		if ((hashes_done && (diff.tv_sec > 0 || diff.tv_usec > 200000)) ||
+		    diff.tv_sec >= opt_log_interval) {
+			hashmeter(thr_id, hashes_done);
+			hashes_done = 0;
+			copy_time(&tv_start, &tv_end);
+		}
+
+		if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED))
+			mt_disable(mythr, thr_id, drv);
+
+		if (mythr->work_update) {
+			drv->update_work(cgpu);
+			mythr->work_update = false;
+		}
+	}
+	cgpu->deven = DEV_DISABLED;
+}
+
+void *miner_thread(void *userdata)
+{
+	struct thr_info *mythr = userdata;
+	const int thr_id = mythr->id;
+	struct cgpu_info *cgpu = mythr->cgpu;
+	struct device_drv *drv = cgpu->drv;
+	char threadname[16];
+
+        snprintf(threadname, sizeof(threadname), "%d/Miner", thr_id);
+	RenameThread(threadname);
+
+	thread_reportout(mythr);
+	if (!drv->thread_init(mythr)) {
+		dev_error(cgpu, REASON_THREAD_FAIL_INIT);
+		goto out;
+	}
+
+	applog(LOG_DEBUG, "Waiting on sem in miner thread");
+	cgsem_wait(&mythr->sem);
+
+	cgpu->last_device_valid_work = time(NULL);
+	drv->hash_work(mythr);
+	drv->thread_shutdown(mythr);
+out:
+	return NULL;
+}
+
+enum {
+	STAT_SLEEP_INTERVAL		= 1,
+	STAT_CTR_INTERVAL		= 10000000,
+	FAILURE_INTERVAL		= 30,
+};
+
+#ifdef HAVE_LIBCURL
+/* Stage another work item from the work returned in a longpoll */
+static void convert_to_work(json_t *val, int rolltime, struct pool *pool, struct timeval *tv_lp, struct timeval *tv_lp_reply)
+{
+	struct work *work;
+	bool rc;
+
+	work = make_work();
+
+	rc = work_decode(pool, work, val);
+	if (unlikely(!rc)) {
+		applog(LOG_ERR, "Could not convert longpoll data to work");
+		free_work(work);
+		return;
+	}
+	total_getworks++;
+	pool->getwork_requested++;
+	work->pool = pool;
+	work->rolltime = rolltime;
+	copy_time(&work->tv_getwork, tv_lp);
+	copy_time(&work->tv_getwork_reply, tv_lp_reply);
+	calc_diff(work, 0);
+
+	if (pool->enabled == POOL_REJECTING)
+		work->mandatory = true;
+
+	if (pool->has_gbt)
+		gen_gbt_work(pool, work);
+	work->longpoll = true;
+	work->getwork_mode = GETWORK_MODE_LP;
+
+	/* We'll be checking this work item twice, but we already know it's
+	 * from a new block so explicitly force the new block detection now
+	 * rather than waiting for it to hit the stage thread. This also
+	 * allows testwork to know whether LP discovered the block or not. */
+	test_work_current(work);
+
+	work = clone_work(work);
+
+	applog(LOG_DEBUG, "Pushing converted work to stage thread");
+
+	stage_work(work);
+	applog(LOG_DEBUG, "Converted longpoll data to work");
+}
+
+/* If we want longpoll, enable it for the chosen default pool, or, if
+ * the pool does not support longpoll, find the first one that does
+ * and use its longpoll support */
+static struct pool *select_longpoll_pool(struct pool *cp)
+{
+	int i;
+
+	if (cp->hdr_path || cp->has_gbt || cp->gbt_solo)
+		return cp;
+	for (i = 0; i < total_pools; i++) {
+		struct pool *pool = pools[i];
+
+		if (pool->has_stratum || pool->hdr_path)
+			return pool;
+	}
+	return NULL;
+}
+#endif /* HAVE_LIBCURL */
+
+/* This will make the longpoll thread wait till it's the current pool, or it
+ * has been flagged as rejecting, before attempting to open any connections.
+ */
+static void wait_lpcurrent(struct pool *pool)
+{
+	while (!cnx_needed(pool) && (pool->enabled == POOL_DISABLED ||
+	       (pool != current_pool() && pool_strategy != POOL_LOADBALANCE &&
+	       pool_strategy != POOL_BALANCE))) {
+		mutex_lock(&lp_lock);
+		pthread_cond_wait(&lp_cond, &lp_lock);
+		mutex_unlock(&lp_lock);
+	}
+}
+
+#ifdef HAVE_LIBCURL
+static void *longpoll_thread(void *userdata)
+{
+	struct pool *cp = (struct pool *)userdata;
+	/* This *pool is the source of the actual longpoll, not the pool we've
+	 * tied it to */
+	struct timeval start, reply, end;
+	struct pool *pool = NULL;
+	char threadname[16];
+	CURL *curl = NULL;
+	int failures = 0;
+	char lpreq[1024];
+	char *lp_url;
+	int rolltime;
+
+	snprintf(threadname, sizeof(threadname), "%d/Longpoll", cp->pool_no);
+	RenameThread(threadname);
+
+retry_pool:
+	pool = select_longpoll_pool(cp);
+	if (!pool) {
+		applog(LOG_WARNING, "No suitable long-poll found for %s", cp->rpc_url);
+		while (!pool) {
+			cgsleep_ms(60000);
+			pool = select_longpoll_pool(cp);
+		}
+	}
+
+	if (pool->has_stratum) {
+		applog(LOG_WARNING, "Block change for %s detection via %s stratum",
+		       cp->rpc_url, pool->rpc_url);
+		goto out;
+	}
+
+	if (pool->gbt_solo) {
+		applog(LOG_WARNING, "Block change for %s detection via getblockcount polling",
+		       cp->rpc_url);
+		while (42) {
+			json_t *val, *res_val = NULL;
+
+			if (unlikely(pool->removed))
+				return NULL;
+
+			cgtime(&start);
+			wait_lpcurrent(cp);
+			sprintf(lpreq, "{\"id\": 0, \"method\": \"getblockcount\"}\n");
+
+			/* We will be making another call immediately after this
+			 * one to get the height so allow this curl to be reused.*/
+			get_gbt_curl(pool, 500);
+			curl_easy_setopt(pool->gbt_curl, CURLOPT_FORBID_REUSE, 0);
+			val = json_rpc_call(pool->gbt_curl, pool->rpc_url, pool->rpc_userpass, lpreq, true,
+					    false, &rolltime, pool, false);
+			release_gbt_curl(pool);
+
+			if (likely(val))
+				res_val = json_object_get(val, "result");
+			if (likely(res_val)) {
+				int height = json_integer_value(res_val);
+				const char *prev_hash;
+
+				failures = 0;
+				json_decref(val);
+				if (height >= cp->height) {
+					applog(LOG_WARNING, "Block height change to %d detected on pool %d",
+					       height, cp->pool_no);
+					update_gbt_solo(pool);
+					continue;
+				}
+
+				sprintf(lpreq, "{\"id\": 0, \"method\": \"getblockhash\", \"params\": [%d]}\n", height);
+				get_gbt_curl(pool, 500);
+				curl_easy_setopt(pool->gbt_curl, CURLOPT_FORBID_REUSE, 1);
+				val = json_rpc_call(pool->gbt_curl, pool->rpc_url, pool->rpc_userpass,
+						    lpreq, true, false, &rolltime, pool, false);
+				release_gbt_curl(pool);
+
+				if (val) {
+					/* Do a comparison on a short stretch of
+					 * the hash to make sure it hasn't changed
+					 * due to mining on an orphan branch. */
+					prev_hash = json_string_value(json_object_get(val, "result"));
+					if (unlikely(prev_hash && strncasecmp(prev_hash + 56, pool->prev_hash, 8))) {
+						applog(LOG_WARNING, "Mining on orphan branch detected, switching!");
+						update_gbt_solo(pool);
+					}
+					json_decref(val);
+				}
+
+				cgsleep_ms(500);
+			} else {
+				if (val)
+					json_decref(val);
+				cgtime(&end);
+				if (end.tv_sec - start.tv_sec > 30)
+					continue;
+				if (failures == 1)
+					applog(LOG_WARNING, "longpoll failed for %s, retrying every 30s", lp_url);
+				cgsleep_ms(30000);
+			}
+		}
+	}
+
+	curl = curl_easy_init();
+	if (unlikely(!curl))
+		quit (1, "Longpoll CURL initialisation failed");
+
+	/* Any longpoll from any pool is enough for this to be true */
+	have_longpoll = true;
+
+	wait_lpcurrent(cp);
+
+	lp_url = pool->rpc_url;
+	applog(LOG_WARNING, "GBT longpoll ID activated for %s", lp_url);
+
+	while (42) {
+		json_t *val, *soval;
+
+		wait_lpcurrent(cp);
+
+		cgtime(&start);
+
+		/* Update the longpollid every time, but do it under lock to
+		 * avoid races */
+		if (pool->has_gbt) {
+			cg_rlock(&pool->gbt_lock);
+			snprintf(lpreq, sizeof(lpreq),
+				"{\"id\": 0, \"method\": \"getblocktemplate\", \"params\": "
+				"[{\"capabilities\": [\"coinbasetxn\", \"workid\", \"coinbase/append\"], "
+				"\"longpollid\": \"%s\"}]}\n", pool->longpollid);
+			cg_runlock(&pool->gbt_lock);
+		}
+
+		/* Longpoll connections can be persistent for a very long time
+		 * and any number of issues could have come up in the meantime
+		 * so always establish a fresh connection instead of relying on
+		 * a persistent one. */
+		curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1);
+		val = json_rpc_call(curl, lp_url, pool->rpc_userpass,
+				    lpreq, false, true, &rolltime, pool, false);
+
+		cgtime(&reply);
+
+		if (likely(val)) {
+			soval = json_object_get(json_object_get(val, "result"), "submitold");
+			if (soval)
+				pool->submit_old = json_is_true(soval);
+			else
+				pool->submit_old = false;
+			convert_to_work(val, rolltime, pool, &start, &reply);
+			failures = 0;
+			json_decref(val);
+		} else {
+			/* Some pools regularly drop the longpoll request so
+			 * only see this as longpoll failure if it happens
+			 * immediately and just restart it the rest of the
+			 * time. */
+			cgtime(&end);
+			if (end.tv_sec - start.tv_sec > 30)
+				continue;
+			if (failures == 1)
+				applog(LOG_WARNING, "longpoll failed for %s, retrying every 30s", lp_url);
+			cgsleep_ms(30000);
+		}
+
+		if (pool != cp) {
+			pool = select_longpoll_pool(cp);
+			if (pool->has_stratum) {
+				applog(LOG_WARNING, "Block change for %s detection via %s stratum",
+				       cp->rpc_url, pool->rpc_url);
+				break;
+			}
+			if (unlikely(!pool))
+				goto retry_pool;
+		}
+
+		if (unlikely(pool->removed))
+			break;
+	}
+
+out:
+	curl_easy_cleanup(curl);
+
+	return NULL;
+}
+#else /* HAVE_LIBCURL */
+static void *longpoll_thread(void __maybe_unused *userdata)
+{
+	pthread_detach(pthread_self());
+	return NULL;
+}
+#endif /* HAVE_LIBCURL */
+
+void reinit_device(struct cgpu_info *cgpu)
+{
+	if (cgpu->deven == DEV_DISABLED)
+		return;
+
+#ifdef USE_USBUTILS
+	/* Attempt a usb device reset if the device has gone sick */
+	if (cgpu->usbdev && cgpu->usbdev->handle)
+		libusb_reset_device(cgpu->usbdev->handle);
+#endif
+	cgpu->drv->reinit_device(cgpu);
+}
+
+static struct timeval rotate_tv;
+
+/* We reap curls if they are unused for over a minute */
+static void reap_curl(struct pool *pool)
+{
+	struct curl_ent *ent, *iter;
+	struct timeval now;
+	int reaped = 0;
+
+	cgtime(&now);
+
+	mutex_lock(&pool->pool_lock);
+	list_for_each_entry_safe(ent, iter, &pool->curlring, node) {
+		if (pool->curls < 2)
+			break;
+		if (now.tv_sec - ent->tv.tv_sec > 300) {
+			reaped++;
+			pool->curls--;
+			list_del(&ent->node);
+			curl_easy_cleanup(ent->curl);
+			free(ent);
+		}
+	}
+	mutex_unlock(&pool->pool_lock);
+
+	if (reaped)
+		applog(LOG_DEBUG, "Reaped %d curl%s from pool %d", reaped, reaped > 1 ? "s" : "", pool->pool_no);
+}
+
+/* Prune old shares we haven't had a response about for over 2 minutes in case
+ * the pool never plans to respond and we're just leaking memory. If we get a
+ * response beyond that time they will be seen as untracked shares. */
+static void prune_stratum_shares(struct pool *pool)
+{
+	struct stratum_share *sshare, *tmpshare;
+	time_t current_time = time(NULL);
+	int cleared = 0;
+
+	mutex_lock(&sshare_lock);
+	HASH_ITER(hh, stratum_shares, sshare, tmpshare) {
+		if (sshare->work->pool == pool && current_time > sshare->sshare_time + 120) {
+			HASH_DEL(stratum_shares, sshare);
+			free_work(sshare->work);
+			free(sshare);
+			cleared++;
+		}
+	}
+	mutex_unlock(&sshare_lock);
+
+	if (cleared) {
+		applog(LOG_WARNING, "Lost %d shares due to no stratum share response from pool %d",
+		       cleared, pool->pool_no);
+		pool->stale_shares += cleared;
+		total_stale += cleared;
+	}
+}
+
+static void *watchpool_thread(void __maybe_unused *userdata)
+{
+	int intervals = 0;
+	cgtimer_t cgt;
+
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+	RenameThread("Watchpool");
+
+	set_lowprio();
+	cgtimer_time(&cgt);
+
+	while (42) {
+		struct timeval now;
+		int i;
+
+		if (++intervals > 120)
+			intervals = 0;
+		cgtime(&now);
+
+		for (i = 0; i < total_pools; i++) {
+			struct pool *pool = pools[i];
+
+			if (!opt_benchmark && !opt_benchfile) {
+				reap_curl(pool);
+				prune_stratum_shares(pool);
+			}
+
+			/* Get a rolling utility per pool over 10 mins */
+			if (intervals > 119) {
+				double shares = pool->diff1 - pool->last_shares;
+
+				pool->last_shares = pool->diff1;
+				pool->utility = (pool->utility + shares * 0.63) / 1.63;
+				pool->shares = pool->utility;
+			}
+
+			if (pool->enabled == POOL_DISABLED)
+				continue;
+
+			/* Don't start testing a pool if its test thread
+			 * from startup is still doing its first attempt. */
+			if (unlikely(pool->testing))
+				continue;
+
+			if (pool_active(pool, true)) {
+				if (pool_tclear(pool, &pool->idle))
+					pool_resus(pool);
+			} else
+				cgtime(&pool->tv_idle);
+
+			/* Only switch pools if the failback pool has been
+			 * alive for more than 5 minutes to prevent
+			 * intermittently failing pools from being used. */
+			if (!pool->idle && pool_strategy == POOL_FAILOVER && pool->prio < cp_prio() &&
+			    now.tv_sec - pool->tv_idle.tv_sec > opt_pool_fallback) {
+				applog(LOG_WARNING, "Pool %d %s stable for >%d seconds",
+				       pool->pool_no, pool->rpc_url, opt_pool_fallback);
+				switch_pools(NULL);
+			}
+		}
+
+		if (current_pool()->idle)
+			switch_pools(NULL);
+
+		if (pool_strategy == POOL_ROTATE && now.tv_sec - rotate_tv.tv_sec > 60 * opt_rotate_period) {
+			cgtime(&rotate_tv);
+			switch_pools(NULL);
+		}
+
+		cgsleep_ms_r(&cgt, 5000);
+		cgtimer_time(&cgt);
+	}
+	return NULL;
+}
+
+/* Makes sure the hashmeter keeps going even if mining threads stall, updates
+ * the screen at regular intervals, and restarts threads if they appear to have
+ * died. */
+#define WATCHDOG_INTERVAL		2
+#define WATCHDOG_SICK_TIME		120
+#define WATCHDOG_DEAD_TIME		600
+#define WATCHDOG_SICK_COUNT		(WATCHDOG_SICK_TIME/WATCHDOG_INTERVAL)
+#define WATCHDOG_DEAD_COUNT		(WATCHDOG_DEAD_TIME/WATCHDOG_INTERVAL)
+
+static void *watchdog_thread(void __maybe_unused *userdata)
+{
+	const unsigned int interval = WATCHDOG_INTERVAL;
+	struct timeval zero_tv;
+
+#ifdef USE_LIBSYSTEMD
+	uint64_t notify_usec;
+	struct timeval notify_interval, notify_tv;
+
+	memset(&notify_interval, 0, sizeof(struct timeval));
+	memset(&notify_tv, 0, sizeof(struct timeval));
+
+	if (sd_watchdog_enabled(false, &notify_usec)) {
+		notify_usec = notify_usec / 2;
+		us_to_timeval(&notify_interval, notify_usec);
+		cgtime(&notify_tv);
+		addtime(&notify_interval, &notify_tv);
+
+		applog(LOG_DEBUG, "Watchdog notify interval: %.3gs",
+				notify_usec / 1000000.0);
+	}
+#endif
+
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+	RenameThread("Watchdog");
+
+	set_lowprio();
+	memset(&zero_tv, 0, sizeof(struct timeval));
+	cgtime(&rotate_tv);
+
+	while (1) {
+		int i;
+		struct timeval now;
+
+		sleep(interval);
+
+		discard_stale();
+
+		hashmeter(-1, 0);
+
+#ifdef HAVE_CURSES
+		if (curses_active_locked()) {
+			struct cgpu_info *cgpu;
+			int count;
+
+			change_logwinsize();
+			curses_print_status();
+			count = 0;
+			for (i = 0; i < total_devices; i++) {
+				cgpu = get_devices(i);
+#ifndef USE_USBUTILS
+				if (cgpu)
+#else
+				if (cgpu && !cgpu->usbinfo.nodev)
+#endif
+					curses_print_devstatus(cgpu, i, count++);
+			}
+#ifdef USE_USBUTILS
+			for (i = 0; i < total_devices; i++) {
+				cgpu = get_devices(i);
+				if (cgpu && cgpu->usbinfo.nodev)
+					curses_print_devstatus(cgpu, i, count++);
+			}
+#endif
+			touchwin(statuswin);
+			wrefresh(statuswin);
+			touchwin(logwin);
+			wrefresh(logwin);
+			unlock_curses();
+		}
+#endif
+
+		cgtime(&now);
+
+#if USE_LIBSYSTEMD
+		if (notify_usec && !time_more(&notify_tv, &now)) {
+			sd_notify(false, "WATCHDOG=1");
+			copy_time(&notify_tv, &now);
+			addtime(&notify_interval, &notify_tv);
+			applog(LOG_DEBUG, "Notified watchdog");
+		}
+#endif
+
+		if (!sched_paused && !should_run()) {
+			applog(LOG_WARNING, "Pausing execution as per stop time %02d:%02d scheduled",
+			       schedstop.tm.tm_hour, schedstop.tm.tm_min);
+			if (!schedstart.enable) {
+				quit(0, "Terminating execution as planned");
+				break;
+			}
+
+			applog(LOG_WARNING, "Will restart execution as scheduled at %02d:%02d",
+			       schedstart.tm.tm_hour, schedstart.tm.tm_min);
+			sched_paused = true;
+
+			rd_lock(&mining_thr_lock);
+			for (i = 0; i < mining_threads; i++)
+				mining_thr[i]->pause = true;
+			rd_unlock(&mining_thr_lock);
+		} else if (sched_paused && should_run()) {
+			applog(LOG_WARNING, "Restarting execution as per start time %02d:%02d scheduled",
+				schedstart.tm.tm_hour, schedstart.tm.tm_min);
+			if (schedstop.enable)
+				applog(LOG_WARNING, "Will pause execution as scheduled at %02d:%02d",
+					schedstop.tm.tm_hour, schedstop.tm.tm_min);
+			sched_paused = false;
+
+			for (i = 0; i < mining_threads; i++) {
+				struct thr_info *thr;
+
+				thr = get_thread(i);
+
+				/* Don't touch disabled devices */
+				if (thr->cgpu->deven == DEV_DISABLED)
+					continue;
+				thr->pause = false;
+				applog(LOG_DEBUG, "Pushing sem post to thread %d", thr->id);
+				cgsem_post(&thr->sem);
+			}
+		}
+
+		for (i = 0; i < total_devices; ++i) {
+			struct cgpu_info *cgpu = get_devices(i);
+			struct thr_info *thr = cgpu->thr[0];
+			enum dev_enable *denable;
+			char dev_str[8];
+
+			if (!thr)
+				continue;
+
+			cgpu->drv->get_stats(cgpu);
+
+			denable = &cgpu->deven;
+			snprintf(dev_str, sizeof(dev_str), "%s %d", cgpu->drv->name, cgpu->device_id);
+
+			/* Thread is waiting on getwork or disabled */
+			if (thr->getwork || *denable == DEV_DISABLED || thr->pause)
+				continue;
+
+			if (cgpu->status != LIFE_WELL && (now.tv_sec - thr->last.tv_sec < WATCHDOG_SICK_TIME)) {
+				if (cgpu->status != LIFE_INIT)
+				applog(LOG_ERR, "%s: Recovered, declaring WELL!", dev_str);
+				cgpu->status = LIFE_WELL;
+				cgpu->device_last_well = time(NULL);
+			} else if (cgpu->status == LIFE_WELL && (now.tv_sec - thr->last.tv_sec > WATCHDOG_SICK_TIME)) {
+				cgpu->rolling = 0;
+				cgpu->status = LIFE_SICK;
+				applog(LOG_ERR, "%s: Idle for more than 60 seconds, declaring SICK!", dev_str);
+				cgtime(&thr->sick);
+
+				dev_error(cgpu, REASON_DEV_SICK_IDLE_60);
+				if (opt_restart) {
+					applog(LOG_ERR, "%s: Attempting to restart", dev_str);
+					reinit_device(cgpu);
+				}
+			} else if (cgpu->status == LIFE_SICK && (now.tv_sec - thr->last.tv_sec > WATCHDOG_DEAD_TIME)) {
+				cgpu->status = LIFE_DEAD;
+				applog(LOG_ERR, "%s: Not responded for more than 10 minutes, declaring DEAD!", dev_str);
+				cgtime(&thr->sick);
+
+				dev_error(cgpu, REASON_DEV_DEAD_IDLE_600);
+			} else if (now.tv_sec - thr->sick.tv_sec > 60 &&
+				   (cgpu->status == LIFE_SICK || cgpu->status == LIFE_DEAD)) {
+				/* Attempt to restart a GPU that's sick or dead once every minute */
+				cgtime(&thr->sick);
+				if (opt_restart)
+					reinit_device(cgpu);
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static void log_print_status(struct cgpu_info *cgpu)
+{
+	char logline[255];
+
+	get_statline(logline, sizeof(logline), cgpu);
+	applog(LOG_WARNING, "%s", logline);
+}
+
+static void noop_get_statline(char __maybe_unused *buf, size_t __maybe_unused bufsiz, struct cgpu_info __maybe_unused *cgpu);
+void blank_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info __maybe_unused *cgpu);
+
+void print_summary(void)
+{
+	struct timeval diff;
+	int hours, mins, secs, i;
+	double utility, displayed_hashes, work_util;
+
+	timersub(&total_tv_end, &total_tv_start, &diff);
+	hours = diff.tv_sec / 3600;
+	mins = (diff.tv_sec % 3600) / 60;
+	secs = diff.tv_sec % 60;
+
+	utility = total_accepted / total_secs * 60;
+	work_util = total_diff1 / total_secs * 60;
+
+	applog(LOG_WARNING, "\nSummary of runtime statistics:\n");
+	applog(LOG_WARNING, "Started at %s", datestamp);
+	if (total_pools == 1)
+		applog(LOG_WARNING, "Pool: %s", pools[0]->rpc_url);
+	applog(LOG_WARNING, "Runtime: %d hrs : %d mins : %d secs", hours, mins, secs);
+	displayed_hashes = total_mhashes_done / total_secs;
+
+	applog(LOG_WARNING, "Average hashrate: %.1f Mhash/s", displayed_hashes);
+	applog(LOG_WARNING, "Solved blocks: %d", found_blocks);
+	applog(LOG_WARNING, "Best share difficulty: %s", best_share);
+	applog(LOG_WARNING, "Share submissions: %"PRId64, total_accepted + total_rejected);
+	applog(LOG_WARNING, "Accepted shares: %"PRId64, total_accepted);
+	applog(LOG_WARNING, "Rejected shares: %"PRId64, total_rejected);
+	applog(LOG_WARNING, "Accepted difficulty shares: %1.f", total_diff_accepted);
+	applog(LOG_WARNING, "Rejected difficulty shares: %1.f", total_diff_rejected);
+	if (total_accepted || total_rejected)
+		applog(LOG_WARNING, "Reject ratio: %.1f%%", (double)(total_rejected * 100) / (double)(total_accepted + total_rejected));
+	applog(LOG_WARNING, "Hardware errors: %d", hw_errors);
+	applog(LOG_WARNING, "Utility (accepted shares / min): %.2f/min", utility);
+	applog(LOG_WARNING, "Work Utility (diff1 shares solved / min): %.2f/min\n", work_util);
+
+	applog(LOG_WARNING, "Stale submissions discarded due to new blocks: %"PRId64, total_stale);
+	applog(LOG_WARNING, "Unable to get work from server occasions: %d", total_go);
+	applog(LOG_WARNING, "Work items generated locally: %d", local_work);
+	applog(LOG_WARNING, "Submitting work remotely delay occasions: %d", total_ro);
+	applog(LOG_WARNING, "New blocks detected on network: %d\n", new_blocks);
+
+	if (total_pools > 1) {
+		for (i = 0; i < total_pools; i++) {
+			struct pool *pool = pools[i];
+
+			applog(LOG_WARNING, "Pool: %s", pool->rpc_url);
+			if (pool->solved)
+				applog(LOG_WARNING, "SOLVED %d BLOCK%s!", pool->solved, pool->solved > 1 ? "S" : "");
+			applog(LOG_WARNING, " Share submissions: %"PRId64, pool->accepted + pool->rejected);
+			applog(LOG_WARNING, " Accepted shares: %"PRId64, pool->accepted);
+			applog(LOG_WARNING, " Rejected shares: %"PRId64, pool->rejected);
+			applog(LOG_WARNING, " Accepted difficulty shares: %1.f", pool->diff_accepted);
+			applog(LOG_WARNING, " Rejected difficulty shares: %1.f", pool->diff_rejected);
+			if (pool->accepted || pool->rejected)
+				applog(LOG_WARNING, " Reject ratio: %.1f%%", (double)(pool->rejected * 100) / (double)(pool->accepted + pool->rejected));
+
+			applog(LOG_WARNING, " Items worked on: %d", pool->works);
+			applog(LOG_WARNING, " Stale submissions discarded due to new blocks: %d", pool->stale_shares);
+			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, "Summary of per device statistics:\n");
+	for (i = 0; i < total_devices; ++i) {
+		struct cgpu_info *cgpu = get_devices(i);
+
+		cgpu->drv->get_statline_before = &blank_get_statline_before;
+		cgpu->drv->get_statline = &noop_get_statline;
+		log_print_status(cgpu);
+	}
+
+	if (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, " ");
+
+	fflush(stderr);
+	fflush(stdout);
+}
+
+static void clean_up(bool restarting)
+{
+#ifdef USE_BITMAIN_SOC
+	struct sysinfo sInfo;
+	if (sysinfo(&sInfo))
+	{
+		applog(LOG_INFO, "Failed to get sysinfo, errno:%u, reason:%s\n",
+			   errno, strerror(errno));
+		total_tv_end_sys=time(NULL);
+	}
+	else
+	{
+		total_tv_end_sys=sInfo.uptime;
+	}
+#endif
+
+#ifdef USE_USBUTILS
+	usb_polling = false;
+	pthread_join(usb_poll_thread, NULL);
+        libusb_exit(NULL);
+#endif
+
+	cgtime(&total_tv_end);
+#ifdef WIN32
+	timeEndPeriod(1);
+#endif
+#ifdef HAVE_CURSES
+	disable_curses();
+#endif
+	if (!restarting && !opt_realquiet && successful_connect)
+		print_summary();
+
+	curl_global_cleanup();
+}
+
+/* Should all else fail and we're unable to clean up threads due to locking
+ * issues etc, just silently exit. */
+static void *killall_thread(void __maybe_unused *arg)
+{
+	pthread_detach(pthread_self());
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+	sleep(5);
+	exit(1);
+	return NULL;
+}
+
+void __quit(int status, bool clean)
+{
+	pthread_t killall_t;
+
+#ifdef USE_LIBSYSTEMD
+	sd_notify(false, "STOPPING=1\n"
+		"STATUS=Shutting down...");
+#endif
+
+	if (unlikely(pthread_create(&killall_t, NULL, killall_thread, NULL)))
+		exit(1);
+
+	if (clean)
+		clean_up(false);
+#ifdef HAVE_CURSES
+	else
+		disable_curses();
+#endif
+
+#if defined(unix) || defined(__APPLE__)
+	if (forkpid > 0) {
+		kill(forkpid, SIGTERM);
+		forkpid = 0;
+	}
+#endif
+	pthread_cancel(killall_t);
+
+	exit(status);
+}
+
+void _quit(int status)
+{
+	__quit(status, true);
+}
+
+#ifdef HAVE_CURSES
+char *curses_input(const char *query)
+{
+	char *input;
+
+	echo();
+	input = cgmalloc(255);
+	leaveok(logwin, false);
+	wlogprint("%s:\n", query);
+	wgetnstr(logwin, input, 255);
+	if (!strlen(input))
+		strcpy(input, "-1");
+	leaveok(logwin, true);
+	noecho();
+	return input;
+}
+#endif
+
+static bool pools_active = false;
+
+static void *test_pool_thread(void *arg)
+{
+	struct pool *pool = (struct pool *)arg;
+
+	if (!pool->blocking)
+		pthread_detach(pthread_self());
+retry:
+	if (pool->removed)
+		goto out;
+	if (pool_active(pool, false)) {
+		pool_tclear(pool, &pool->idle);
+		bool first_pool = false;
+
+		cg_wlock(&control_lock);
+		if (!pools_active) {
+			currentpool = pool;
+			if (pool->pool_no != 0)
+				first_pool = true;
+			pools_active = true;
+		}
+		cg_wunlock(&control_lock);
+
+		if (unlikely(first_pool))
+			applog(LOG_NOTICE, "Switching to pool %d %s - first alive pool", pool->pool_no, pool->rpc_url);
+
+		pool_resus(pool);
+		switch_pools(NULL);
+	} else {
+		pool_died(pool);
+		if (!pool->blocking) {
+			sleep(5);
+			goto retry;
+		}
+	}
+
+	pool->testing = false;
+out:
+	return NULL;
+}
+
+/* Always returns true that the pool details were added unless we are not
+ * live, implying this is the only pool being added, so if no pools are
+ * active it returns false. */
+bool add_pool_details(struct pool *pool, bool live, char *url, char *user, char *pass)
+{
+	size_t siz;
+
+	pool->rpc_url = url;
+	pool->rpc_user = user;
+	pool->rpc_pass = pass;
+	siz = strlen(pool->rpc_user) + strlen(pool->rpc_pass) + 2;
+	pool->rpc_userpass = cgmalloc(siz);
+	snprintf(pool->rpc_userpass, siz, "%s:%s", pool->rpc_user, pool->rpc_pass);
+
+	pool->testing = true;
+	pool->idle = true;
+	pool->blocking = !live;
+	enable_pool(pool);
+
+	pthread_create(&pool->test_thread, NULL, test_pool_thread, (void *)pool);
+	if (!live) {
+		pthread_join(pool->test_thread, NULL);
+		return pools_active;
+	}
+	return true;
+}
+
+#ifdef HAVE_CURSES
+static bool input_pool(bool live)
+{
+	char *url, *user, *pass;
+	struct pool *pool;
+	bool ret = false;
+
+	immedok(logwin, true);
+	wlogprint("Input server details.\n");
+
+retry:
+	url = NULL;
+	user = NULL;
+	pass = NULL;
+	url = curses_input("URL");
+	if (!strcmp(url, "-1")) {
+		wlogprint("Invalid input\n");
+		goto out;
+	}
+
+	user = curses_input("Username");
+	if (!strcmp(user, "-1")) {
+		wlogprint("Invalid input\n");
+		goto out;
+	}
+
+	pass = curses_input("Password [enter for none]");
+	if (!strcmp(pass, "-1")) {
+		free(pass);
+		pass = strdup("");
+	}
+
+	pool = add_pool();
+	url = setup_url(pool, url);
+	ret = add_pool_details(pool, live, url, user, pass);
+	if (!ret) {
+		remove_pool(pool);
+		wlogprint("URL %s failed alive testing, reinput details\n", url);
+		free(url);
+		free(user);
+		free(pass);
+		goto retry;
+	}
+out:
+	immedok(logwin, false);
+
+	if (!ret) {
+		free(url);
+		free(user);
+		free(pass);
+	}
+	return ret;
+}
+#endif
+
+#if defined(unix) || defined(__APPLE__)
+static void fork_monitor()
+{
+	// Make a pipe: [readFD, writeFD]
+	int pfd[2];
+	int r = pipe(pfd);
+
+	if (r < 0) {
+		perror("pipe - failed to create pipe for --monitor");
+		exit(1);
+	}
+
+	// Make stderr write end of pipe
+	fflush(stderr);
+	r = dup2(pfd[1], 2);
+	if (r < 0) {
+		perror("dup2 - failed to alias stderr to write end of pipe for --monitor");
+		exit(1);
+	}
+	r = close(pfd[1]);
+	if (r < 0) {
+		perror("close - failed to close write end of pipe for --monitor");
+		exit(1);
+	}
+
+	// Don't allow a dying monitor to kill the main process
+	sighandler_t sr0 = signal(SIGPIPE, SIG_IGN);
+	sighandler_t sr1 = signal(SIGPIPE, SIG_IGN);
+	if (SIG_ERR == sr0 || SIG_ERR == sr1) {
+		perror("signal - failed to edit signal mask for --monitor");
+		exit(1);
+	}
+
+	// Fork a child process
+	forkpid = fork();
+	if (forkpid < 0) {
+		perror("fork - failed to fork child process for --monitor");
+		exit(1);
+	}
+
+	// Child: launch monitor command
+	if (0 == forkpid) {
+		// Make stdin read end of pipe
+		r = dup2(pfd[0], 0);
+		if (r < 0) {
+			perror("dup2 - in child, failed to alias read end of pipe to stdin for --monitor");
+			exit(1);
+		}
+		close(pfd[0]);
+		if (r < 0) {
+			perror("close - in child, failed to close read end of  pipe for --monitor");
+			exit(1);
+		}
+
+		// Launch user specified command
+		execl("/bin/bash", "/bin/bash", "-c", opt_stderr_cmd, (char*)NULL);
+		perror("execl - in child failed to exec user specified command for --monitor");
+		exit(1);
+	}
+
+	// Parent: clean up unused fds and bail
+	r = close(pfd[0]);
+	if (r < 0) {
+		perror("close - failed to close read end of pipe for --monitor");
+		exit(1);
+	}
+}
+#endif // defined(unix)
+
+#ifdef HAVE_CURSES
+static void enable_curses_windows(void)
+{
+	int x,y;
+
+	getmaxyx(mainwin, y, x);
+	statuswin = newwin(logstart, x, 0, 0);
+	leaveok(statuswin, true);
+	logwin = newwin(y - logcursor, 0, logcursor, 0);
+	idlok(logwin, true);
+	scrollok(logwin, true);
+	leaveok(logwin, true);
+	cbreak();
+	noecho();
+}
+void enable_curses(void) {
+	lock_curses();
+	if (curses_active) {
+		unlock_curses();
+		return;
+	}
+
+	mainwin = initscr();
+	enable_curses_windows();
+	curses_active = true;
+	statusy = logstart;
+	unlock_curses();
+}
+#endif
+
+static int cgminer_id_count = 0;
+
+/* Various noop functions for drivers that don't support or need their
+ * variants. */
+static void noop_reinit_device(struct cgpu_info __maybe_unused *cgpu)
+{
+}
+
+void blank_get_statline_before(char __maybe_unused *buf,size_t __maybe_unused bufsiz, struct cgpu_info __maybe_unused *cgpu)
+{
+}
+
+static void noop_get_statline(char __maybe_unused *buf, size_t __maybe_unused bufsiz, struct cgpu_info __maybe_unused *cgpu)
+{
+}
+
+static bool noop_get_stats(struct cgpu_info __maybe_unused *cgpu)
+{
+	return true;
+}
+
+static bool noop_thread_prepare(struct thr_info __maybe_unused *thr)
+{
+	return true;
+}
+
+static uint64_t noop_can_limit_work(struct thr_info __maybe_unused *thr)
+{
+	return 0xffffffff;
+}
+
+static bool noop_thread_init(struct thr_info __maybe_unused *thr)
+{
+	return true;
+}
+
+static bool noop_prepare_work(struct thr_info __maybe_unused *thr, struct work __maybe_unused *work)
+{
+	return true;
+}
+
+static void noop_hw_error(struct thr_info __maybe_unused *thr)
+{
+}
+
+static void noop_thread_shutdown(struct thr_info __maybe_unused *thr)
+{
+}
+
+static void noop_thread_enable(struct thr_info __maybe_unused *thr)
+{
+}
+
+static void noop_detect(bool __maybe_unused hotplug)
+{
+}
+
+static struct api_data *noop_get_api_stats(struct cgpu_info __maybe_unused *cgpu)
+{
+	return NULL;
+}
+
+static void noop_hash_work(struct thr_info __maybe_unused *thr)
+{
+}
+
+static void generic_zero_stats(struct cgpu_info *cgpu)
+{
+	cgpu->diff_accepted =
+	cgpu->diff_rejected =
+	cgpu->hw_errors = 0;
+}
+
+#define noop_flush_work noop_reinit_device
+#define noop_update_work noop_reinit_device
+#define noop_queue_full noop_get_stats
+#define noop_identify_device noop_reinit_device
+
+/* Fill missing driver drv functions with noops */
+void fill_device_drv(struct device_drv *drv)
+{
+	if (!drv->drv_detect)
+		drv->drv_detect = &noop_detect;
+	if (!drv->reinit_device)
+		drv->reinit_device = &noop_reinit_device;
+	if (!drv->get_statline_before)
+		drv->get_statline_before = &blank_get_statline_before;
+	if (!drv->get_statline)
+		drv->get_statline = &noop_get_statline;
+	if (!drv->get_stats)
+		drv->get_stats = &noop_get_stats;
+	if (!drv->thread_prepare)
+		drv->thread_prepare = &noop_thread_prepare;
+	if (!drv->can_limit_work)
+		drv->can_limit_work = &noop_can_limit_work;
+	if (!drv->thread_init)
+		drv->thread_init = &noop_thread_init;
+	if (!drv->prepare_work)
+		drv->prepare_work = &noop_prepare_work;
+	if (!drv->hw_error)
+		drv->hw_error = &noop_hw_error;
+	if (!drv->thread_shutdown)
+		drv->thread_shutdown = &noop_thread_shutdown;
+	if (!drv->thread_enable)
+		drv->thread_enable = &noop_thread_enable;
+	if (!drv->hash_work)
+		drv->hash_work = &hash_sole_work;
+	if (!drv->flush_work)
+		drv->flush_work = &noop_flush_work;
+	if (!drv->update_work)
+		drv->update_work = &noop_update_work;
+	if (!drv->queue_full)
+		drv->queue_full = &noop_queue_full;
+	if (!drv->zero_stats)
+		drv->zero_stats = &generic_zero_stats;
+	/* If drivers support internal diff they should set a max_diff or
+	 * we will assume they don't and set max to 1. */
+	if (!drv->max_diff)
+		drv->max_diff = 1;
+	if (!drv->genwork)
+		opt_gen_stratum_work = true;
+}
+
+void null_device_drv(struct device_drv *drv)
+{
+	drv->drv_detect = &noop_detect;
+	drv->reinit_device = &noop_reinit_device;
+	drv->get_statline_before = &blank_get_statline_before;
+	drv->get_statline = &noop_get_statline;
+	drv->get_api_stats = &noop_get_api_stats;
+	drv->get_stats = &noop_get_stats;
+	drv->identify_device = &noop_identify_device;
+	drv->set_device = NULL;
+
+	drv->thread_prepare = &noop_thread_prepare;
+	drv->can_limit_work = &noop_can_limit_work;
+	drv->thread_init = &noop_thread_init;
+	drv->prepare_work = &noop_prepare_work;
+
+	/* This should make the miner thread just exit */
+	drv->hash_work = &noop_hash_work;
+
+	drv->hw_error = &noop_hw_error;
+	drv->thread_shutdown = &noop_thread_shutdown;
+	drv->thread_enable = &noop_thread_enable;
+
+	drv->zero_stats = &generic_zero_stats;
+
+	drv->hash_work = &noop_hash_work;
+
+	drv->queue_full = &noop_queue_full;
+	drv->flush_work = &noop_flush_work;
+	drv->update_work = &noop_update_work;
+
+	drv->max_diff = 1;
+	drv->min_diff = 1;
+}
+
+void enable_device(struct cgpu_info *cgpu)
+{
+	cgpu->deven = DEV_ENABLED;
+
+	wr_lock(&devices_lock);
+	devices[cgpu->cgminer_id = cgminer_id_count++] = cgpu;
+	wr_unlock(&devices_lock);
+
+	if (hotplug_mode)
+		new_threads += cgpu->threads;
+	else
+		mining_threads += cgpu->threads;
+
+	rwlock_init(&cgpu->qlock);
+	cgpu->queued_work = NULL;
+}
+
+struct _cgpu_devid_counter {
+	char name[4];
+	int lastid;
+	UT_hash_handle hh;
+};
+
+static void adjust_mostdevs(void)
+{
+	if (total_devices - zombie_devs > most_devices)
+		most_devices = total_devices - zombie_devs;
+}
+
+#ifdef USE_ICARUS
+bool icarus_get_device_id(struct cgpu_info *cgpu)
+{
+	static struct _cgpu_devid_counter *devids = NULL;
+	struct _cgpu_devid_counter *d;
+
+	HASH_FIND_STR(devids, cgpu->drv->name, d);
+	if (d)
+		return (d->lastid + 1);
+	else
+		return 0;
+}
+#endif
+
+bool add_cgpu(struct cgpu_info *cgpu)
+{
+	static struct _cgpu_devid_counter *devids = NULL;
+	struct _cgpu_devid_counter *d;
+
+	HASH_FIND_STR(devids, cgpu->drv->name, d);
+	if (d)
+		cgpu->device_id = ++d->lastid;
+	else {
+		d = cgmalloc(sizeof(*d));
+		cg_memcpy(d->name, cgpu->drv->name, sizeof(d->name));
+		cgpu->device_id = d->lastid = 0;
+		HASH_ADD_STR(devids, name, d);
+	}
+
+	wr_lock(&devices_lock);
+	devices = cgrealloc(devices, sizeof(struct cgpu_info *) * (total_devices + new_devices + 2));
+	wr_unlock(&devices_lock);
+
+	mutex_lock(&stats_lock);
+	cgpu->last_device_valid_work = time(NULL);
+	mutex_unlock(&stats_lock);
+
+	if (hotplug_mode)
+		devices[total_devices + new_devices++] = cgpu;
+	else
+		devices[total_devices++] = cgpu;
+
+	adjust_mostdevs();
+#ifdef USE_USBUTILS
+	if (cgpu->usbdev && !cgpu->unique_id && cgpu->usbdev->serial_string &&
+	    strlen(cgpu->usbdev->serial_string) > 4)
+		cgpu->unique_id = str_text(cgpu->usbdev->serial_string);
+#endif
+	return true;
+}
+
+struct device_drv *copy_drv(struct device_drv *drv)
+{
+	struct device_drv *copy;
+
+	copy = cgmalloc(sizeof(*copy));
+	cg_memcpy(copy, drv, sizeof(*copy));
+	copy->copy = true;
+	return copy;
+}
+
+#ifdef USE_USBUTILS
+static void hotplug_process(void)
+{
+	struct thr_info *thr;
+	int i, j;
+
+	for (i = 0; i < new_devices; i++) {
+		struct cgpu_info *cgpu;
+		int dev_no = total_devices + i;
+
+		cgpu = devices[dev_no];
+		enable_device(cgpu);
+		cgpu->cgminer_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET;
+		cgpu->rolling = cgpu->total_mhashes = 0;
+	}
+
+	wr_lock(&mining_thr_lock);
+	mining_thr = cgrealloc(mining_thr, sizeof(thr) * (mining_threads + new_threads + 1));
+	for (i = 0; i < new_threads; i++)
+		mining_thr[mining_threads + i] = cgcalloc(1, sizeof(*thr));
+
+	// Start threads
+	for (i = 0; i < new_devices; ++i) {
+		struct cgpu_info *cgpu = devices[total_devices];
+		cgpu->thr = cgmalloc(sizeof(*cgpu->thr) * (cgpu->threads+1));
+		cgpu->thr[cgpu->threads] = NULL;
+		cgpu->status = LIFE_INIT;
+		cgtime(&(cgpu->dev_start_tv));
+
+		for (j = 0; j < cgpu->threads; ++j) {
+			thr = __get_thread(mining_threads);
+			thr->id = mining_threads;
+			thr->cgpu = cgpu;
+			thr->device_thread = j;
+
+			if (!cgpu->drv->thread_prepare(thr)) {
+				null_device_drv(cgpu->drv);
+				cgpu->deven = DEV_DISABLED;
+				continue;
+			}
+
+			if (unlikely(thr_info_create(thr, NULL, miner_thread, thr)))
+				quit(1, "hotplug thread %d create failed", thr->id);
+
+			cgpu->thr[j] = thr;
+
+			/* Enable threads for devices set not to mine but disable
+			 * their queue in case we wish to enable them later */
+			if (cgpu->deven != DEV_DISABLED) {
+				applog(LOG_DEBUG, "Pushing sem post to thread %d", thr->id);
+				cgsem_post(&thr->sem);
+			}
+
+			mining_threads++;
+		}
+		total_devices++;
+		applog(LOG_WARNING, "Hotplug: %s added %s %i", cgpu->drv->dname, cgpu->drv->name, cgpu->device_id);
+	}
+	wr_unlock(&mining_thr_lock);
+
+	adjust_mostdevs();
+#ifdef HAVE_CURSES
+	switch_logsize(true);
+#endif
+}
+
+#define DRIVER_DRV_DETECT_HOTPLUG(X) X##_drv.drv_detect(true);
+
+static void reinit_usb(void)
+{
+	int err;
+
+	usb_reinit = true;
+	/* Wait till libusb_poll_thread is no longer polling */
+	while (polling_usb)
+		cgsleep_ms(100);
+
+	applog(LOG_DEBUG, "Reinitialising libusb");
+	libusb_exit(NULL);
+	err = libusb_init(NULL);
+	if (err)
+		quit(1, "Reinit of libusb failed err %d:%s", err, libusb_error_name(err));
+	usb_reinit = false;
+}
+
+static void *hotplug_thread(void __maybe_unused *userdata)
+{
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+	RenameThread("Hotplug");
+
+	hotplug_mode = true;
+
+	cgsleep_ms(5000);
+
+	while (0x2a) {
+// Version 0.1 just add the devices on - worry about using nodev later
+
+		if (hotplug_time == 0)
+			cgsleep_ms(5000);
+		else {
+			new_devices = 0;
+			new_threads = 0;
+
+			/* Use the DRIVER_PARSE_COMMANDS macro to detect all
+			 * devices */
+			DRIVER_PARSE_COMMANDS(DRIVER_DRV_DETECT_HOTPLUG)
+
+			if (new_devices)
+				hotplug_process();
+
+			/* If we have no active devices, libusb may need to
+			 * be re-initialised to work properly */
+			if (total_devices == zombie_devs)
+				reinit_usb();
+
+			// hotplug_time >0 && <=9999
+			cgsleep_ms(hotplug_time * 1000);
+		}
+	}
+
+	return NULL;
+}
+#endif
+
+static void probe_pools(void)
+{
+	int i;
+
+	for (i = 0; i < total_pools; i++) {
+		struct pool *pool = pools[i];
+
+		pool->testing = true;
+		pthread_create(&pool->test_thread, NULL, test_pool_thread, (void *)pool);
+	}
+}
+
+#define DRIVER_FILL_DEVICE_DRV(X) fill_device_drv(&X##_drv);
+#define DRIVER_DRV_DETECT_ALL(X) X##_drv.drv_detect(false);
+
+#ifdef USE_USBUTILS
+static void *libusb_poll_thread(void __maybe_unused *arg)
+{
+	struct timeval tv_end;
+
+	RenameThread("USBPoll");
+
+	while (likely(usb_polling)) {
+		tv_end.tv_sec = 0;
+		tv_end.tv_usec = 100000;
+		while (usb_reinit) {
+			polling_usb = false;
+			cgsleep_ms(100);
+		}
+		polling_usb = true;
+		libusb_handle_events_timeout_completed(NULL, &tv_end, NULL);
+	}
+
+	/* Cancel any cancellable usb transfers */
+	cancel_usb_transfers();
+
+	/* Keep event handling going until there are no async transfers in
+	 * flight. */
+	while (async_usb_transfers()) {
+		tv_end.tv_sec = 0;
+		tv_end.tv_usec = 0;
+		libusb_handle_events_timeout_completed(NULL, &tv_end, NULL);
+	};
+
+	return NULL;
+}
+
+static void initialise_usb(void) {
+	int err = libusb_init(NULL);
+
+	if (err) {
+		fprintf(stderr, "libusb_init() failed err %d", err);
+		fflush(stderr);
+		quit(1, "libusb_init() failed");
+	}
+	initialise_usblocks();
+	usb_polling = true;
+	pthread_create(&usb_poll_thread, NULL, libusb_poll_thread, NULL);
+}
+#else
+#define initialise_usb() {}
+#endif
+
+#ifdef USE_BITMAIN_SOC
+void setStartTimePoint()
+{
+	char logstr[256];
+	struct sysinfo sInfo;
+	if (sysinfo(&sInfo))
+	{
+		sprintf(logstr, "Failed to get sysinfo, errno:%u, reason:%s\n",
+				errno, strerror(errno));
+		writeInitLogFile(logstr);
+
+		total_tv_start_sys=time(NULL);
+		total_tv_end_sys=total_tv_start_sys+1;
+	}
+	else
+	{
+		total_tv_start_sys=sInfo.uptime;
+		total_tv_end_sys=total_tv_start_sys+1;
+
+		sprintf(logstr, "setStartTimePoint total_tv_start_sys=%d total_tv_end_sys=%d\n",total_tv_start_sys, total_tv_end_sys);
+		writeInitLogFile(logstr);
+	}
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+	struct sigaction handler;
+	struct work *work = NULL;
+	bool pool_msg = false;
+	struct thr_info *thr;
+	struct block *block;
+	int i, j, slept = 0;
+	unsigned int k;
+	char *s;
+
+	/* This dangerous functions tramples random dynamically allocated
+	 * variables so do it before anything at all */
+	if (unlikely(curl_global_init(CURL_GLOBAL_ALL)))
+		early_quit(1, "Failed to curl_global_init");
+
+#ifdef USE_LIBSYSTEMD
+	sd_notify(false, "STATUS=Starting up...");
+#endif
+
+# ifdef __linux
+	/* If we're on a small lowspec platform with only one CPU, we should
+	 * yield after dropping a lock to allow a thread waiting for it to be
+	 * able to get CPU time to grab the lock. */
+	if (sysconf(_SC_NPROCESSORS_ONLN) == 1)
+		selective_yield = &sched_yield;
+#endif
+
+#if LOCK_TRACKING
+	// Must be first
+	if (unlikely(pthread_mutex_init(&lockstat_lock, NULL)))
+		quithere(1, "Failed to pthread_mutex_init lockstat_lock errno=%d", errno);
+#endif
+
+	initial_args = cgmalloc(sizeof(char *) * (argc + 1));
+	for  (i = 0; i < argc; i++)
+		initial_args[i] = strdup(argv[i]);
+	initial_args[argc] = NULL;
+
+	mutex_init(&hash_lock);
+	mutex_init(&console_lock);
+	cglock_init(&control_lock);
+	mutex_init(&stats_lock);
+	mutex_init(&sharelog_lock);
+	cglock_init(&ch_lock);
+	mutex_init(&sshare_lock);
+	rwlock_init(&blk_lock);
+	rwlock_init(&netacc_lock);
+	rwlock_init(&mining_thr_lock);
+	rwlock_init(&devices_lock);
+#if STRATUM_WORK_TIMING
+	cglock_init(&swt_lock);
+#endif
+
+	mutex_init(&lp_lock);
+	if (unlikely(pthread_cond_init(&lp_cond, NULL)))
+		early_quit(1, "Failed to pthread_cond_init lp_cond");
+
+	mutex_init(&restart_lock);
+	if (unlikely(pthread_cond_init(&restart_cond, NULL)))
+		early_quit(1, "Failed to pthread_cond_init restart_cond");
+
+	if (unlikely(pthread_cond_init(&gws_cond, NULL)))
+		early_quit(1, "Failed to pthread_cond_init gws_cond");
+
+	/* Create a unique get work queue */
+	getq = tq_new();
+	if (!getq)
+		early_quit(1, "Failed to create getq");
+	/* We use the getq mutex as the staged lock */
+	stgd_lock = &getq->mutex;
+
+	initialise_usb();
+
+	snprintf(packagename, sizeof(packagename), "%s %s", PACKAGE, VERSION);
+
+	handler.sa_handler = &sighandler;
+	handler.sa_flags = 0;
+	sigemptyset(&handler.sa_mask);
+	sigaction(SIGTERM, &handler, &termhandler);
+	sigaction(SIGINT, &handler, &inthandler);
+	sigaction(SIGABRT, &handler, &abrthandler);
+#ifndef WIN32
+	signal(SIGPIPE, SIG_IGN);
+#else
+	timeBeginPeriod(1);
+#endif
+	opt_kernel_path = alloca(PATH_MAX);
+	strcpy(opt_kernel_path, CGMINER_PREFIX);
+	cgminer_path = alloca(PATH_MAX);
+	s = strdup(argv[0]);
+	strcpy(cgminer_path, dirname(s));
+	free(s);
+	strcat(cgminer_path, "/");
+
+	devcursor = 8;
+	logstart = devcursor + 1;
+	logcursor = logstart + 1;
+
+	block = cgcalloc(sizeof(struct block), 1);
+	for (i = 0; i < 36; i++)
+		strcat(block->hash, "0");
+	HASH_ADD_STR(blocks, hash, block);
+	strcpy(current_hash, block->hash);
+
+	INIT_LIST_HEAD(&scan_devices);
+
+	/* parse command line */
+	opt_register_table(opt_config_table,
+			   "Options for both config file and command line");
+	opt_register_table(opt_cmdline_table,
+			   "Options for command line only");
+
+	opt_parse(&argc, argv, applog_and_exit);
+	if (argc != 1)
+		early_quit(1, "Unexpected extra commandline arguments");
+
+	if (!config_loaded)
+		load_default_config();
+
+	// use this to test diff value handing on various builds and architectures.
+	// since share submission depends on the difficulty calculated vs the pool
+	// work requirement, if this test fails, cgminer could discard a block due
+	// to the difficulty calculation being wrong and too low vs the pool work
+	if (opt_blockcheck)
+	{
+// how many bits are skipped for diffone
+#define BC_DIFF1_BITS 32
+// number of bits in a hash
+#define BC_MAX_BITS 256
+// number of bits set to 1 for diffone
+#define BC_TEST_BITS 16
+#define HEX_BYTE 8
+// ratio limits on the difference between actual and test
+#define BC_DELTA_PLUS_LIM 0.00000001
+#define BC_DELTA_MINUS_LIM (-0.00000001)
+
+		struct work test_work;
+
+#define ASSERTbc(condition) __maybe_unused static char sizeof_work_hash_must_be_32[(condition)?1:-1]
+ASSERTbc(sizeof(test_work.hash) == (BC_MAX_BITS / HEX_BYTE));
+
+		double test, delta, ratio, change, powval, powdelta, powratio;
+		double diff = 1.0, prevdiff = 0.0, prevtest = 0.0;
+		bool deltabad, diffbad, fail = false;
+		int i, byte, j;
+
+		for (i = BC_DIFF1_BITS; i < BC_MAX_BITS - BC_TEST_BITS; i += HEX_BYTE)
+		{
+			byte = ((BC_MAX_BITS - 1) - i) / HEX_BYTE;
+
+			// set the hash value to 0000...00ffff00...0000
+
+			for (j = 0; j < (BC_MAX_BITS / HEX_BYTE); j++)
+				test_work.hash[j] = 0;
+
+			// BC_TEST_BITS
+			test_work.hash[byte] = 0xff;
+			test_work.hash[byte - 1] = 0xff;
+
+			// Of course MUST use the same calculation as all diff value tests
+			//  use to decide when to submit shares
+			test = truediffone / le256todouble(test_work.hash);
+
+			delta = diff - test;
+			ratio = delta / diff;
+
+			if (ratio < BC_DELTA_MINUS_LIM || ratio > BC_DELTA_PLUS_LIM)
+			{
+				deltabad = true;
+				fail = true;
+			}
+			else
+				deltabad = false;
+
+			powval = pow(2.0, (double)(i - BC_DIFF1_BITS));
+			powdelta = diff - powval;
+			powratio = powdelta / powval;
+
+			if (powratio < BC_DELTA_MINUS_LIM || powratio > BC_DELTA_PLUS_LIM)
+			{
+				diffbad = true;
+				fail = true;
+			}
+			else
+				diffbad = false;
+
+			if (diff < prevdiff || test < prevtest)
+				fail = true;
+
+			printf("%3d%s%s real=%.8E calc=%.8E delta=%.8E%s pow=%.8E powdelta=%.8E%s\n",
+				i, (diff < prevdiff) ? " FATAL DROP IN DIFF:": "",
+				(test < prevtest) ? " FATAL DROP IN TEST:": "", diff,
+				test, delta, deltabad ? " DELTA TOO LARGE!" : "",
+				powval, powdelta, diffbad ? " POW DELTA TOO LARGE!" : "");
+
+			prevtest = test;
+			prevdiff = diff;
+
+			diff *= (1 << HEX_BYTE);
+
+			change = diff / prevdiff;
+			if (change != (1 << HEX_BYTE))
+			{
+				fail = true;
+				printf("FATAL for %d double size doesn't handle above %.8E\n", i, prevdiff);
+			}
+		}
+		if (fail)
+			printf("\nTEST FAILED! See above.\n");
+		else
+			printf("\nTest succeeded.\n");
+
+		return 0;
+	}
+
+	if (opt_benchmark || opt_benchfile) {
+		struct pool *pool;
+
+		pool = add_pool();
+		pool->rpc_url = cgmalloc(255);
+		if (opt_benchfile)
+			strcpy(pool->rpc_url, "Benchfile");
+		else
+			strcpy(pool->rpc_url, "Benchmark");
+		pool->rpc_user = pool->rpc_url;
+		pool->rpc_pass = pool->rpc_url;
+		pool->rpc_userpass = pool->rpc_url;
+		pool->sockaddr_url = pool->rpc_url;
+		strncpy(pool->diff, "?", sizeof(pool->diff)-1);
+		pool->diff[sizeof(pool->diff)-1] = '\0';
+		enable_pool(pool);
+		pool->idle = false;
+		successful_connect = true;
+
+		for (i = 0; i < 16; i++) {
+			hex2bin(&bench_hidiff_bins[i][0], &bench_hidiffs[i][0], 160);
+			hex2bin(&bench_lodiff_bins[i][0], &bench_lodiffs[i][0], 160);
+		}
+		set_target(bench_target, 32);
+	}
+
+#ifdef USE_BITMAIN_SOC
+	if(opt_version_path)
+	{
+		FILE * fpversion = fopen(opt_version_path, "rb");
+		char tmp[256] = {0};
+		int len = 0;
+		char * start = 0;
+
+		if(fpversion == NULL)
+		{
+			applog(LOG_ERR, "Open miner version file %s error", opt_version_path);
+		}
+		else
+		{
+			len = fread(tmp, 1, 256, fpversion);
+
+			if(len <= 0)
+			{
+				applog(LOG_ERR, "Read miner version file %s error %d", opt_version_path, len);
+			}
+			else
+			{
+				start = strstr(tmp, "\n");
+
+				if(start == NULL)
+				{
+					strcpy(g_miner_compiletime, tmp);
+				}
+				else
+				{
+					cg_memcpy(g_miner_compiletime, tmp, start-tmp);
+					strcpy(g_miner_type, start+1);
+				}
+
+				if(g_miner_compiletime[strlen(g_miner_compiletime)-1] == '\n')
+				{
+					g_miner_compiletime[strlen(g_miner_compiletime)-1] = 0;
+				}
+
+				if(g_miner_compiletime[strlen(g_miner_compiletime)-1] == '\r')
+				{
+					g_miner_compiletime[strlen(g_miner_compiletime)-1] = 0;
+				}
+
+				if(g_miner_type[strlen(g_miner_type)-1] == '\n')
+				{
+					g_miner_type[strlen(g_miner_type)-1] = 0;
+				}
+
+				if(g_miner_type[strlen(g_miner_type)-1] == '\r')
+				{
+					g_miner_type[strlen(g_miner_type)-1] = 0;
+				}
+			}
+		}
+		applog(LOG_ERR, "Miner compile time: %s type: %s", g_miner_compiletime, g_miner_type);
+	}
+#endif
+
+#ifdef HAVE_CURSES
+	if (opt_realquiet || opt_display_devs || opt_decode)
+		use_curses = false;
+
+	if (use_curses)
+		enable_curses();
+#endif
+
+	applog(LOG_WARNING, "Started %s", packagename);
+	if (cnfbuf) {
+		applog(LOG_NOTICE, "Loaded configuration file %s", cnfbuf);
+		switch (fileconf_load) {
+			case 0:
+				applog(LOG_WARNING, "Fatal JSON error in configuration file.");
+				applog(LOG_WARNING, "Configuration file could not be used.");
+				break;
+			case -1:
+				applog(LOG_WARNING, "Error in configuration file, partially loaded.");
+				if (use_curses)
+					applog(LOG_WARNING, "Start cgminer with -T to see what failed to load.");
+				break;
+			default:
+				break;
+		}
+		free(cnfbuf);
+		cnfbuf = NULL;
+	}
+
+	strcat(opt_kernel_path, "/");
+
+	if (want_per_device_stats)
+		opt_log_output = true;
+
+#ifdef HAVE_SYSLOG_H
+	if (opt_log_output)
+		setlogmask(LOG_UPTO(LOG_DEBUG));
+	else
+		setlogmask(LOG_UPTO(LOG_NOTICE));
+#endif
+
+	total_control_threads = 8;
+	control_thr = cgcalloc(total_control_threads, sizeof(*thr));
+
+	gwsched_thr_id = 0;
+
+#ifdef USE_AVALON7
+	if (opt_avalon7_ssplus_enable) {
+		ssp_sorter_init(HT_SIZE, HT_PRB_LMT, HT_PRB_C1, HT_PRB_C2);
+		ssp_hasher_init();
+	}
+#endif
+#ifdef USE_USBUTILS
+	usb_initialise();
+
+	// before device detection
+	cgsem_init(&usb_resource_sem);
+	usbres_thr_id = 1;
+	thr = &control_thr[usbres_thr_id];
+	if (thr_info_create(thr, NULL, usb_resource_thread, thr))
+		early_quit(1, "usb resource thread create failed");
+	pthread_detach(thr->pth);
+#endif
+
+	/* Use the DRIVER_PARSE_COMMANDS macro to fill all the device_drvs */
+	DRIVER_PARSE_COMMANDS(DRIVER_FILL_DEVICE_DRV)
+
+	if (!total_pools) {
+		applog(LOG_WARNING, "Need to specify at least one pool server.");
+#ifdef HAVE_CURSES
+		if (!use_curses || !input_pool(false))
+#endif
+			early_quit(1, "Pool setup failed");
+	}
+
+	for (i = 0; i < total_pools; i++) {
+		struct pool *pool = pools[i];
+		size_t siz;
+
+		pool->cgminer_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET;
+		pool->cgminer_pool_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET;
+
+		if (!pool->rpc_userpass) {
+			if (!pool->rpc_pass)
+				pool->rpc_pass = strdup("");
+			if (!pool->rpc_user)
+				early_quit(1, "No login credentials supplied for pool %u %s", i, pool->rpc_url);
+			siz = strlen(pool->rpc_user) + strlen(pool->rpc_pass) + 2;
+			pool->rpc_userpass = cgmalloc(siz);
+			snprintf(pool->rpc_userpass, siz, "%s:%s", pool->rpc_user, pool->rpc_pass);
+		}
+	}
+	/* Set the currentpool to pool 0 */
+	currentpool = pools[0];
+
+#ifdef HAVE_SYSLOG_H
+	if (use_syslog)
+		openlog(PACKAGE, LOG_PID, LOG_USER);
+#endif
+
+	#if defined(unix) || defined(__APPLE__)
+		if (opt_stderr_cmd)
+			fork_monitor();
+	#endif // defined(unix)
+
+	mining_thr = cgcalloc(mining_threads, sizeof(thr));
+	for (i = 0; i < mining_threads; i++)
+		mining_thr[i] = cgcalloc(1, sizeof(*thr));
+
+	// Start threads
+	k = 0;
+	for (i = 0; i < total_devices; ++i) {
+		struct cgpu_info *cgpu = devices[i];
+		cgpu->thr = cgmalloc(sizeof(*cgpu->thr) * (cgpu->threads+1));
+		cgpu->thr[cgpu->threads] = NULL;
+		cgpu->status = LIFE_INIT;
+
+		for (j = 0; j < cgpu->threads; ++j, ++k) {
+			thr = get_thread(k);
+			thr->id = k;
+			thr->cgpu = cgpu;
+			thr->device_thread = j;
+
+			if (!cgpu->drv->thread_prepare(thr))
+				continue;
+
+			if (unlikely(thr_info_create(thr, NULL, miner_thread, thr)))
+				early_quit(1, "thread %d create failed", thr->id);
+
+			cgpu->thr[j] = thr;
+
+			/* Enable threads for devices set not to mine but disable
+			 * their queue in case we wish to enable them later */
+			if (cgpu->deven != DEV_DISABLED) {
+				applog(LOG_DEBUG, "Pushing sem post to thread %d", thr->id);
+				cgsem_post(&thr->sem);
+			}
+		}
+	}
+
+	if (opt_benchmark || opt_benchfile)
+		goto begin_bench;
+
+	for (i = 0; i < total_pools; i++) {
+		struct pool *pool  = pools[i];
+
+		enable_pool(pool);
+		pool->idle = true;
+	}
+
+	/* Look for at least one active pool before starting */
+	applog(LOG_NOTICE, "Probing for an alive pool");
+	probe_pools();
+	do {
+		sleep(1);
+		slept++;
+	} while (!pools_active && slept < 60);
+
+	while (!pools_active) {
+		if (!pool_msg) {
+			applog(LOG_ERR, "No servers were found that could be used to get work from.");
+			applog(LOG_ERR, "Please check the details from the list below of the servers you have input");
+			applog(LOG_ERR, "Most likely you have input the wrong URL, forgotten to add a port, or have not set up workers");
+			for (i = 0; i < total_pools; i++) {
+				struct pool *pool = pools[i];
+
+				applog(LOG_WARNING, "Pool: %d  URL: %s  User: %s  Password: %s",
+				i, pool->rpc_url, pool->rpc_user, pool->rpc_pass);
+			}
+			pool_msg = true;
+			if (use_curses)
+				applog(LOG_ERR, "Press any key to exit, or cgminer will wait indefinitely for an alive pool.");
+		}
+		if (!use_curses)
+			early_quit(0, "No servers could be used! Exiting.");
+#ifdef HAVE_CURSES
+		touchwin(logwin);
+		wrefresh(logwin);
+		halfdelay(10);
+		if (getch() != ERR)
+			early_quit(0, "No servers could be used! Exiting.");
+		cbreak();
+#endif
+	};
+
+begin_bench:
+	/* Use the DRIVER_PARSE_COMMANDS macro to detect all devices */
+	DRIVER_PARSE_COMMANDS(DRIVER_DRV_DETECT_ALL)
+
+	if (opt_display_devs) {
+		applog(LOG_ERR, "Devices detected:");
+		for (i = 0; i < total_devices; ++i) {
+			struct cgpu_info *cgpu = devices[i];
+			if (cgpu->name)
+				applog(LOG_ERR, " %2d. %s %d: %s (driver: %s)", i, cgpu->drv->name, cgpu->device_id, cgpu->name, cgpu->drv->dname);
+			else
+				applog(LOG_ERR, " %2d. %s %d (driver: %s)", i, cgpu->drv->name, cgpu->device_id, cgpu->drv->dname);
+		}
+		early_quit(0, "%d devices listed", total_devices);
+	}
+
+	mining_threads = 0;
+	for (i = 0; i < total_devices; ++i)
+		enable_device(devices[i]);
+
+	mining_thr = cgcalloc(mining_threads, sizeof(thr));
+	for (i = 0; i < mining_threads; i++)
+		mining_thr[i] = cgcalloc(1, sizeof(*thr));
+
+
+	if (!opt_decode) {
+#ifdef USE_USBUTILS
+		if (!total_devices) {
+			applog(LOG_WARNING, "No devices detected!");
+			applog(LOG_WARNING, "Waiting for USB hotplug devices or press q to quit");
+		}
+#else
+		if (!total_devices)
+			early_quit(1, "All devices disabled, cannot mine!");
+#endif
+	}
+
+	most_devices = total_devices;
+
+	load_temp_cutoffs();
+
+	for (i = 0; i < total_devices; ++i)
+		devices[i]->cgminer_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET;
+
+	if (!opt_compact) {
+		logstart += most_devices;
+		logcursor = logstart + 1;
+#ifdef HAVE_CURSES
+		check_winsizes();
+#endif
+	}
+
+	// Start threads
+	k = 0;
+	for (i = 0; i < total_devices; ++i) {
+		struct cgpu_info *cgpu = devices[i];
+		cgpu->thr = cgmalloc(sizeof(*cgpu->thr) * (cgpu->threads+1));
+		cgpu->thr[cgpu->threads] = NULL;
+		cgpu->status = LIFE_INIT;
+
+		for (j = 0; j < cgpu->threads; ++j, ++k) {
+			thr = get_thread(k);
+			thr->id = k;
+			thr->cgpu = cgpu;
+			thr->device_thread = j;
+
+			if (!cgpu->drv->thread_prepare(thr))
+				continue;
+
+			if (unlikely(thr_info_create(thr, NULL, miner_thread, thr)))
+				early_quit(1, "thread %d create failed", thr->id);
+
+			cgpu->thr[j] = thr;
+
+			/* Enable threads for devices set not to mine but disable
+			 * their queue in case we wish to enable them later */
+			if (cgpu->deven != DEV_DISABLED) {
+				applog(LOG_DEBUG, "Pushing sem post to thread %d", thr->id);
+				cgsem_post(&thr->sem);
+			}
+		}
+	}
+
+	total_mhashes_done = 0;
+	for (i = 0; i < total_devices; i++) {
+		struct cgpu_info *cgpu = devices[i];
+
+		cgpu->rolling = cgpu->total_mhashes = 0;
+	}
+
+	cgtime_real(&total_tv_start);
+	get_datestamp(datestamp, sizeof(datestamp), &total_tv_start);
+
+#ifdef USE_BITMAIN_SOC
+	struct sysinfo sInfo;
+	if (sysinfo(&sInfo))
+	{
+		applog(LOG_INFO, "Failed to get sysinfo, errno:%u, reason:%s\n",
+			   errno, strerror(errno));
+		total_tv_end_sys=time(NULL);
+		total_tv_start_sys=time(NULL);
+	}
+	else
+	{
+		total_tv_end_sys=sInfo.uptime;
+		total_tv_start_sys=sInfo.uptime;
+	}
+#endif
+	cgtime(&total_tv_start);
+	cgtime(&total_tv_end);
+	cgtime(&tv_hashmeter);
+
+	watchpool_thr_id = 2;
+	thr = &control_thr[watchpool_thr_id];
+	/* start watchpool thread */
+	if (thr_info_create(thr, NULL, watchpool_thread, NULL))
+		early_quit(1, "watchpool thread create failed");
+	pthread_detach(thr->pth);
+
+	watchdog_thr_id = 3;
+	thr = &control_thr[watchdog_thr_id];
+	/* start watchdog thread */
+	if (thr_info_create(thr, NULL, watchdog_thread, NULL))
+		early_quit(1, "watchdog thread create failed");
+	pthread_detach(thr->pth);
+
+	/* Create API socket thread */
+	api_thr_id = 5;
+	thr = &control_thr[api_thr_id];
+	if (thr_info_create(thr, NULL, api_thread, thr))
+		early_quit(1, "API thread create failed");
+
+#ifdef USE_USBUTILS
+	hotplug_thr_id = 6;
+	thr = &control_thr[hotplug_thr_id];
+	if (thr_info_create(thr, NULL, hotplug_thread, thr))
+		early_quit(1, "hotplug thread create failed");
+	pthread_detach(thr->pth);
+#endif
+
+#ifdef HAVE_CURSES
+	/* 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 all previous threads. */
+	input_thr_id = 7;
+	thr = &control_thr[input_thr_id];
+	if (thr_info_create(thr, NULL, input_thread, thr))
+		early_quit(1, "input thread create failed");
+	pthread_detach(thr->pth);
+#endif
+
+	/* Just to be sure */
+	if (total_control_threads != 8)
+		early_quit(1, "incorrect total_control_threads (%d) should be 8", total_control_threads);
+
+#ifdef USE_GEKKO
+	set_lowprio();
+#else
+	set_highprio();
+#endif
+
+#ifdef USE_LIBSYSTEMD
+	sd_notify(false, "READY=1\n"
+		"STATUS=Started");
+#endif
+
+	/* Once everything is set up, main() becomes the getwork scheduler */
+	while (42) {
+		int ts, max_staged = max_queue;
+		struct pool *pool;
+
+		if (opt_work_update)
+			signal_work_update();
+		opt_work_update = false;
+#if defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_AVALON9) || defined(USE_AVALONLC3)
+		if (opt_clean_jobs) {
+			signal_clean_jobs();
+			opt_clean_jobs = false;
+		}
+#endif
+		mutex_lock(stgd_lock);
+		ts = __total_staged();
+		/* Wait until hash_pop tells us we need to create more work */
+		if (ts > max_staged) {
+			work_filled = true;
+			pthread_cond_wait(&gws_cond, stgd_lock);
+			ts = __total_staged();
+		}
+		mutex_unlock(stgd_lock);
+
+		if (ts > max_staged) {
+			/* Keeps slowly generating work even if it's not being
+			 * used to keep last_getwork incrementing and to see
+			 * if pools are still alive. */
+			work_filled = true;
+			work = hash_pop(false);
+			if (work)
+				discard_work(work);
+			continue;
+		}
+
+		if (work)
+			discard_work(work);
+		work = make_work();
+
+		while (42) {
+			pool = select_pool();
+			if (!pool_unusable(pool))
+				break;
+			switch_pools(NULL);
+			pool = select_pool();
+			if (pool_unusable(pool))
+				cgsleep_ms(5);
+		};
+		if (pool->has_stratum) {
+			if (opt_gen_stratum_work) {
+				gen_stratum_work(pool, work);
+				applog(LOG_DEBUG, "Generated stratum work");
+				stage_work(work);
+			}
+			continue;
+		}
+
+#ifdef HAVE_LIBCURL
+		if (pool->gbt_solo) {
+			gen_solo_work(pool, work);
+			applog(LOG_DEBUG, "Generated GBT SOLO work");
+			stage_work(work);
+			continue;
+		}
+
+		if (pool->has_gbt) {
+			gen_gbt_work(pool, work);
+			applog(LOG_DEBUG, "Generated GBT work");
+			stage_work(work);
+			continue;
+		}
+#endif
+		if (opt_benchfile) {
+			get_benchfile_work(work);
+			applog(LOG_DEBUG, "Generated benchfile work");
+			stage_work(work);
+			continue;
+		} else if (opt_benchmark) {
+			get_benchmark_work(work);
+			applog(LOG_DEBUG, "Generated benchmark work");
+			stage_work(work);
+			continue;
+		}
+	}
+
+	return 0;
+}

+ 89 - 0
compat.h

@@ -0,0 +1,89 @@
+#ifndef __COMPAT_H__
+#define __COMPAT_H__
+
+#ifdef WIN32
+#include "config.h"
+#include <errno.h>
+#include <time.h>
+#include <pthread.h>
+#include <sys/time.h>
+
+#include "miner.h"  // for timersub
+#include "util.h"
+
+#include <winsock2.h>
+#include <windows.h>
+
+#ifndef HAVE_LIBWINPTHREAD
+static inline int nanosleep(const struct timespec *req, struct timespec *rem)
+{
+	struct timeval tstart;
+	DWORD msecs;
+
+	cgtime(&tstart);
+	msecs = (req->tv_sec * 1000) + ((999999 + req->tv_nsec) / 1000000);
+
+	if (SleepEx(msecs, true) == WAIT_IO_COMPLETION) {
+		if (rem) {
+			struct timeval tdone, tnow, tleft;
+			tdone.tv_sec = tstart.tv_sec + req->tv_sec;
+			tdone.tv_usec = tstart.tv_usec + ((999 + req->tv_nsec) / 1000);
+			if (tdone.tv_usec > 1000000) {
+				tdone.tv_usec -= 1000000;
+				++tdone.tv_sec;
+			}
+
+			cgtime(&tnow);
+			if (timercmp(&tnow, &tdone, >))
+				return 0;
+			timersub(&tdone, &tnow, &tleft);
+
+			rem->tv_sec = tleft.tv_sec;
+			rem->tv_nsec = tleft.tv_usec * 1000;
+		}
+		errno = EINTR;
+		return -1;
+	}
+	return 0;
+}
+#endif
+#if 0
+static inline int sleep(unsigned int secs)
+{
+	struct timespec req, rem;
+	req.tv_sec = secs;
+	req.tv_nsec = 0;
+	if (!nanosleep(&req, &rem))
+		return 0;
+	return rem.tv_sec + (rem.tv_nsec ? 1 : 0);
+}
+#endif
+enum {
+	PRIO_PROCESS		= 0,
+};
+
+static inline int setpriority(__maybe_unused int which, __maybe_unused int who, __maybe_unused int prio)
+{
+	/* FIXME - actually do something */
+	return 0;
+}
+
+typedef unsigned long int ulong;
+typedef unsigned short int ushort;
+typedef unsigned int uint;
+
+#ifndef __SUSECONDS_T_TYPE
+typedef long suseconds_t;
+#endif
+
+#ifdef HAVE_LIBWINPTHREAD
+#define PTH(thr) ((thr)->pth)
+#else
+#define PTH(thr) ((thr)->pth.p)
+#endif
+
+#else
+#define PTH(thr) ((thr)->pth)
+#endif /* WIN32 */
+
+#endif /* __COMPAT_H__ */

+ 3 - 0
compat/.gitignore

@@ -0,0 +1,3 @@
+libusb-1.0/libusb/libusb-1.0.la
+libusb-1.0/libusb/*.lo
+libusb-1.0/libusb/os/*.lo

+ 11 - 0
compat/Makefile.am

@@ -0,0 +1,11 @@
+SUBDIRS =
+
+if WANT_STATIC_JANSSON
+SUBDIRS	+= jansson-2.9
+endif
+
+if WANT_USBUTILS
+if WANT_STATIC_LIBUSB
+SUBDIRS += libusb-1.0
+endif
+endif

+ 796 - 0
compat/jansson-2.9/CHANGES

@@ -0,0 +1,796 @@
+Version 2.9
+===========
+
+Released 2016-09-18
+
+* New features:
+
+  - Add ``json_auto_t`` to automatically decref a value that goes out
+    of scope. Available only on GCC and Clang. (#301)
+
+* Build:
+
+  - Fix CMake build (at least on Linux) by removing conflicting
+    jansson_config.h from the distribution (#306)
+
+  - Change CMake install target generation to be optional (#305)
+
+* Documentation:
+
+  - Small documentation fixes.
+
+
+Version 2.8
+===========
+
+Released 2016-08-30
+
+* New features:
+
+  - Always preserve insertion order of object items.
+    `json_object_iter()` and friends, `json_object_foreach()` and
+    `json_dumps()` and friends now always work in the insertion order of
+    object items (#293).
+
+  - Add `json_object_foreach_safe()` macro that allows
+    `json_object_del()` calls during iteration (#230).
+
+  - Add `json_get_alloc_funcs()` to allow reading the allocation
+    functions set by `json_set_alloc_funcs()` (#262, #264).
+
+  - Add `json_pack()` format specifiers s?, o? and O? for values that
+    can be null (#261, #270).
+
+* Bug fixes:
+
+  - Fix a crash when parsing inputs consisting of very deeply nested
+    arrays or objects (#282, #284).
+
+  - Never convert numbers to integers in the parser when
+    JSON_DECODE_INT_AS_REAL is set. This fixes error messages for
+    overflowing numbers when JSON_DECODE_INT_AS_REAL is set (#212).
+
+  - Fix a use-after-free in `json_pack()` error handling.
+
+  - Fix subnormal number parsing on mingw32.
+
+  - Handle out-of-memory situations gracefully in the hashtable
+    implementation (#298).
+
+* Build:
+
+  - Fix build with CMake on all versions of Visual Studio up to 2015
+    (#262, #289).
+
+  - Fix pkgconfig libdir when using CMake (#268).
+
+  - Fix CMake config for static CRT builds on Windows (#206).
+
+  - Fix warnings on LLVM 6.0 targeting iOS arm64 (#208).
+
+  - Add coverlls.io support via Travis for a nice test coverage badge
+    (#211).
+
+  - Don't expect ``jansson_config.h`` to be in the compiler's include
+    path (#209).
+
+  - Add a build-time option to set initial hashtable size (#213).
+
+  - Use snprintf and strncpy in place of sprintf and strcpy to silence
+    linker warnings on OpenBSD (#233).
+
+* Documentation:
+
+  - Fix various typos in documentation, and a broken link (#258).
+
+  - Add an example program in ``examples/`` (#214, #217).
+
+  - Fix building of documentation man pages (#207).
+
+  - Document the fact that copying objects doesn't preserve the
+    insertion order of keys (#237).
+
+* Tests:
+
+  - Don't use the nonstandard __FUNCTION__ macro in tests.
+
+  - Use expr instead of $((...)) in shell scripts for Solaris 10
+    compatibility.
+
+  - Disable Visual Studio warning C4756 when triggered deliberately in
+    tests (#216).
+
+  - Other minor fixes (#221, #248).
+
+* Other changes:
+
+  - List all unrecognized object keys when strict unpacking fails
+    (#263).
+
+  - Alter the order of the members of the hashtable_pair struct for
+    easier debugging.
+
+  - Minor performance improvement to `json_dump()` and friends (#234).
+
+  - Minor style fixes (#255, #257).
+
+
+Version 2.7
+===========
+
+Released 2014-10-02
+
+* New features:
+
+  - `json_pack()` and friends: Add format specifiers ``s%`` and ``+%``
+    for a size_t string length (#141).
+
+  - `json_unpack()` and friends: Add format specifier ``s%`` for
+    unpacking the string length along with the string itself (#141).
+
+  - Add length-aware string constructors `json_stringn()` and
+    `json_stringn_nocheck()`, length-aware string mutators
+    `json_string_setn()` and `json_string_setn_nocheck()`, and a
+    function for getting string's length `json_string_length()` (#141,
+    #143).
+
+  - Support ``\u0000`` escapes in the decoder. The support can be
+    enabled by using the ``JSON_ALLOW_NUL`` decoding flag (#141).
+
+  - Add `json_boolean_value()` as an alias for `json_is_true()`
+    (#146).
+
+  - Add JSON_REAL_PRECISION encoding flag/macro for controlling real
+    number precision (#178).
+
+  - Define the maximum indentation as JSON_MAX_INDENT (#191).
+
+* Bug fixes:
+
+  - Some malformed ``\uNNNN`` escapes could crash the decoder with an
+    assertion failure.
+
+  - Avoid integer overflows with very long strings in UTF-8 decoder and
+    hashtable.
+
+  - Check for *NULL* key in `json_object_get()` and
+    `json_object_del()` (#151).
+
+  - Enhance hashtable seeding on Windows (#162).
+
+  - `json_unpack()`: Allow mixing JSON_STRICT with optional keys
+    (#162, #163).
+
+  - Fix int/int32 mismatch (#142).
+
+  - Parse subnormal numbers correctly (#202).
+
+* Build:
+
+  - Remove VS2010 build files. CMake should be used on Windows instead
+    (#165).
+
+  - Fix CMake build flags for MinGW (#193).
+
+  - Add CMake config files for find_package. Rename config.h to
+    jansson_private_config.h (#157, #159).
+
+  - Make Valgrind checks work with CMake (#160).
+
+  - Fix feature checks to use correct __ATOMIC flags.
+
+  - Fix CMake checks for uint16_t and uint8_t support (#177).
+
+  - Make Jansson build on SmartOS/Solaris (#171).
+
+  - Work around a GCC bug on Solaris (#175).
+
+  - Fix autoreconf on Debian (#182).
+
+  - Don't use GNU make specific export for global AM_CFLAGS (#203,
+    #204).
+
+  - Fix building on Android using the supplied Android.mk (#166,
+    #174).
+
+  - Android.mk: Add -DHAVE_STDINT_H to LOCAL_CFLAGS (#200).
+
+* Documentation:
+
+  - Document JANSSON_BUILD_SHARED_LIBS CMake option (#187).
+
+* Tests:
+
+  - Close file handles correctly (#198).
+
+* Other changes:
+
+  - ``\uNNNN`` escapes are now encoded in upper case for better
+    readability.
+
+  - Enable usage of AddressSanitizer (#180).
+
+
+Version 2.6
+===========
+
+Released 2014-02-11
+
+* Security:
+
+  - CVE-2013-6401: The hash function used by the hashtable
+    implementation has been changed, and is automatically seeded with
+    random data when the first JSON object is created. This prevents
+    an attacker from causing large JSON objects with specially crafted
+    keys perform poorly.
+
+* New features:
+
+  - `json_object_seed()`: Set the seed value of the hash function.
+
+* Bug fixes:
+
+  - Include CMake specific files in the release tarball.
+
+* Documentation:
+
+  - Fix tutorial source to send a User-Agent header, which is now
+    required by the GitHub API.
+
+  - Set all memory to zero in secure_free() example.
+
+
+Version 2.5
+===========
+
+Released 2013-09-19
+
+* New features:
+
+  - `json_pack()` and friends: Add format specifiers ``s#``, ``+`` and
+    ``+#``.
+
+  - Add ``JSON_DECODE_INT_AS_REAL`` decoding flag to treat all numbers
+    as real in the decoder (#123).
+
+  - Add `json_array_foreach()`, paralleling `json_object_foreach()`
+    (#118).
+
+* Bug fixes:
+
+  - `json_dumps()` and friends: Don't crash if json is *NULL* and
+    ``JSON_ENCODE_ANY`` is set.
+
+  - Fix a theoretical integer overflow in `jsonp_strdup()`.
+
+  - Fix `l_isxdigit()` macro (#97).
+
+  - Fix an off-by-one error in `json_array_remove()`.
+
+* Build:
+
+  - Support CMake in addition to GNU Autotools (#106, #107, #112,
+    #115, #120, #127).
+
+  - Support building for Android (#109).
+
+  - Don't use ``-Werror`` by default.
+
+  - Support building and testing with VPATH (#93).
+
+  - Fix compilation when ``NDEBUG`` is defined (#128)
+
+* Tests:
+
+  - Fix a refleak in ``test/bin/json_process.c``.
+
+* Documentation:
+
+  - Clarify the return value of `json_load_callback_t`.
+
+  - Document how to circumvent problems with separate heaps on Windows.
+
+  - Fix memory leaks and warnings in ``github_commits.c``.
+
+  - Use `json_decref()` properly in tutorial.
+
+* Other:
+
+  - Make it possible to forward declare ``struct json_t``.
+
+
+Version 2.4
+===========
+
+Released 2012-09-23
+
+* New features:
+
+  - Add `json_boolean()` macro that returns the JSON true or false
+    value based on its argument (#86).
+
+  - Add `json_load_callback()` that calls a callback function
+    repeatedly to read the JSON input (#57).
+
+  - Add JSON_ESCAPE_SLASH encoding flag to escape all occurences of
+    ``/`` with ``\/``.
+
+* Bug fixes:
+
+  - Check for and reject NaN and Inf values for reals. Encoding these
+    values resulted in invalid JSON.
+
+  - Fix `json_real_set()` to return -1 on error.
+
+* Build:
+
+  - Jansson now builds on Windows with Visual Studio 2010, and
+    includes solution and project files in ``win32/vs2010/``
+    directory.
+
+  - Fix build warnings (#77, #78).
+
+  - Add ``-no-undefined`` to LDFLAGS (#90).
+
+* Tests:
+
+  - Fix the symbol exports test on Linux/PPC64 (#88).
+
+* Documentation:
+
+  - Fix typos (#73, #84).
+
+
+Version 2.3.1
+=============
+
+Released 2012-04-20
+
+* Build issues:
+
+  - Only use ``long long`` if ``strtoll()`` is also available.
+
+* Documentation:
+
+  - Fix the names of library version constants in documentation. (#52)
+
+  - Change the tutorial to use GitHub API v3. (#65)
+
+* Tests:
+
+  - Make some tests locale independent. (#51)
+
+  - Distribute the library exports test in the tarball.
+
+  - Make test run on shells that don't support the ``export FOO=bar``
+    syntax.
+
+
+Version 2.3
+===========
+
+Released 2012-01-27
+
+* New features:
+
+  - `json_unpack()` and friends: Add support for optional object keys
+    with the ``{s?o}`` syntax.
+
+  - Add `json_object_update_existing()` and
+    `json_object_update_missing()`, for updating only existing keys or
+    only adding missing keys to an object. (#37)
+
+  - Add `json_object_foreach()` for more convenient iteration over
+    objects. (#45, #46)
+
+  - When decoding JSON, write the number of bytes that were read from
+    input to ``error.position`` also on success. This is handy with
+    ``JSON_DISABLE_EOF_CHECK``.
+
+  - Add support for decoding any JSON value, not just arrays or
+    objects. The support is enabled with the new ``JSON_DECODE_ANY``
+    flag. Patch by Andrea Marchesini. (#4)
+
+* Bug fixes
+
+  - Avoid problems with object's serial number growing too big. (#40,
+    #41)
+
+  - Decoding functions now return NULL if the first argument is NULL.
+    Patch by Andrea Marchesini.
+
+  - Include ``jansson_config.h.win32`` in the distribution tarball.
+
+  - Remove ``+`` and leading zeros from exponents in the encoder.
+    (#39)
+
+  - Make Jansson build and work on MinGW. (#39, #38)
+
+* Documentation
+
+  - Note that the same JSON values must not be encoded in parallel by
+    separate threads. (#42)
+
+  - Document MinGW support.
+
+
+Version 2.2.1
+=============
+
+Released 2011-10-06
+
+* Bug fixes:
+
+  - Fix real number encoding and decoding under non-C locales. (#32)
+
+  - Fix identifier decoding under non-UTF-8 locales. (#35)
+
+  - `json_load_file()`: Open the input file in binary mode for maximum
+    compatiblity.
+
+* Documentation:
+
+  - Clarify the lifecycle of the result of the ``s`` fromat of
+    `json_unpack()`. (#31)
+
+  - Add some portability info. (#36)
+
+  - Little clarifications here and there.
+
+* Other:
+
+  - Some style fixes, issues detected by static analyzers.
+
+
+Version 2.2
+===========
+
+Released 2011-09-03
+
+* New features:
+
+  - `json_dump_callback()`: Pass the encoder output to a callback
+    function in chunks.
+
+* Bug fixes:
+
+  - `json_string_set()`: Check that target is a string and value is
+    not NULL.
+
+* Other:
+
+  - Documentation typo fixes and clarifications.
+
+
+Version 2.1
+===========
+
+Released 2011-06-10
+
+* New features:
+
+  - `json_loadb()`: Decode a string with a given size, useful if the
+    string is not null terminated.
+
+  - Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON
+    value. By default, only arrays and objects can be encoded. (#19)
+
+  - Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding
+    error if any JSON object in the input contins duplicate keys. (#3)
+
+  - Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a
+    valid JSON input. This allows other data after the JSON data.
+
+* Bug fixes:
+
+  - Fix an additional memory leak when memory allocation fails in
+    `json_object_set()` and friends.
+
+  - Clear errno before calling `strtod()` for better portability. (#27)
+
+* Building:
+
+  - Avoid set-but-not-used warning/error in a test. (#20)
+
+* Other:
+
+  - Minor clarifications to documentation.
+
+
+Version 2.0.1
+=============
+
+Released 2011-03-31
+
+* Bug fixes:
+
+  - Replace a few `malloc()` and `free()` calls with their
+    counterparts that support custom memory management.
+
+  - Fix object key hashing in json_unpack() strict checking mode.
+
+  - Fix the parentheses in ``JANSSON_VERSION_HEX`` macro.
+
+  - Fix `json_object_size()` return value.
+
+  - Fix a few compilation issues.
+
+* Portability:
+
+  - Enhance portability of `va_copy()`.
+
+  - Test framework portability enhancements.
+
+* Documentation:
+
+  - Distribute ``doc/upgrading.rst`` with the source tarball.
+
+  - Build documentation in strict mode in ``make distcheck``.
+
+
+Version 2.0
+===========
+
+Released 2011-02-28
+
+This release is backwards incompatible with the 1.x release series.
+See the chapter "Upgrading from older versions" in documentation for
+details.
+
+* Backwards incompatible changes:
+
+  - Unify unsigned integer usage in the API: All occurences of
+    unsigned int and unsigned long have been replaced with size_t.
+
+  - Change JSON integer's underlying type to the widest signed integer
+    type available, i.e. long long if it's supported, otherwise long.
+    Add a typedef json_int_t that defines the type.
+
+  - Change the maximum indentation depth to 31 spaces in encoder. This
+    frees up bits from the flags parameter of encoding functions
+    `json_dumpf()`, `json_dumps()` and `json_dump_file()`.
+
+  - For future needs, add a flags parameter to all decoding functions
+    `json_loadf()`, `json_loads()` and `json_load_file()`.
+
+* New features
+
+  - `json_pack()`, `json_pack_ex()`, `json_vpack_ex()`: Create JSON
+    values based on a format string.
+
+  - `json_unpack()`, `json_unpack_ex()`, `json_vunpack_ex()`: Simple
+    value extraction and validation functionality based on a format
+    string.
+
+  - Add column, position and source fields to the ``json_error_t``
+    struct.
+
+  - Enhance error reporting in the decoder.
+
+  - ``JANSSON_VERSION`` et al.: Preprocessor constants that define the
+    library version.
+
+  - `json_set_alloc_funcs()`: Set custom memory allocation functions.
+
+* Fix many portability issues, especially on Windows.
+
+* Configuration
+
+  - Add file ``jansson_config.h`` that contains site specific
+    configuration. It's created automatically by the configure script,
+    or can be created by hand if the configure script cannot be used.
+    The file ``jansson_config.h.win32`` can be used without
+    modifications on Windows systems.
+
+  - Add a section to documentation describing how to build Jansson on
+    Windows.
+
+  - Documentation now requires Sphinx 1.0 or newer.
+
+
+Version 1.3
+===========
+
+Released 2010-06-13
+
+* New functions:
+
+  - `json_object_iter_set()`, `json_object_iter_set_new()`: Change
+    object contents while iterating over it.
+
+  - `json_object_iter_at()`: Return an iterator that points to a
+    specific object item.
+
+* New encoding flags:
+
+  - ``JSON_PRESERVE_ORDER``: Preserve the insertion order of object
+    keys.
+
+* Bug fixes:
+
+  - Fix an error that occured when an array or object was first
+    encoded as empty, then populated with some data, and then
+    re-encoded
+
+  - Fix the situation like above, but when the first encoding resulted
+    in an error
+
+* Documentation:
+
+  - Clarify the documentation on reference stealing, providing an
+    example usage pattern
+
+
+Version 1.2.1
+=============
+
+Released 2010-04-03
+
+* Bug fixes:
+
+  - Fix reference counting on ``true``, ``false`` and ``null``
+  - Estimate real number underflows in decoder with 0.0 instead of
+    issuing an error
+
+* Portability:
+
+  - Make ``int32_t`` available on all systems
+  - Support compilers that don't have the ``inline`` keyword
+  - Require Autoconf 2.60 (for ``int32_t``)
+
+* Tests:
+
+  - Print test names correctly when ``VERBOSE=1``
+  - ``test/suites/api``: Fail when a test fails
+  - Enhance tests for iterators
+  - Enhance tests for decoding texts that contain null bytes
+
+* Documentation:
+
+  - Don't remove ``changes.rst`` in ``make clean``
+  - Add a chapter on RFC conformance
+
+
+Version 1.2
+===========
+
+Released 2010-01-21
+
+* New functions:
+
+  - `json_equal()`: Test whether two JSON values are equal
+  - `json_copy()` and `json_deep_copy()`: Make shallow and deep copies
+    of JSON values
+  - Add a version of all functions taking a string argument that
+    doesn't check for valid UTF-8: `json_string_nocheck()`,
+    `json_string_set_nocheck()`, `json_object_set_nocheck()`,
+    `json_object_set_new_nocheck()`
+
+* New encoding flags:
+
+  - ``JSON_SORT_KEYS``: Sort objects by key
+  - ``JSON_ENSURE_ASCII``: Escape all non-ASCII Unicode characters
+  - ``JSON_COMPACT``: Use a compact representation with all unneeded
+    whitespace stripped
+
+* Bug fixes:
+
+  - Revise and unify whitespace usage in encoder: Add spaces between
+    array and object items, never append newline to output.
+  - Remove const qualifier from the ``json_t`` parameter in
+    `json_string_set()`, `json_integer_set()` and `json_real_set`.
+  - Use ``int32_t`` internally for representing Unicode code points
+    (int is not enough on all platforms)
+
+* Other changes:
+
+  - Convert ``CHANGES`` (this file) to reStructured text and add it to
+    HTML documentation
+  - The test system has been refactored. Python is no longer required
+    to run the tests.
+  - Documentation can now be built by invoking ``make html``
+  - Support for pkg-config
+
+
+Version 1.1.3
+=============
+
+Released 2009-12-18
+
+* Encode reals correctly, so that first encoding and then decoding a
+  real always produces the same value
+* Don't export private symbols in ``libjansson.so``
+
+
+Version 1.1.2
+=============
+
+Released 2009-11-08
+
+* Fix a bug where an error message was not produced if the input file
+  could not be opened in `json_load_file()`
+* Fix an assertion failure in decoder caused by a minus sign without a
+  digit after it
+* Remove an unneeded include of ``stdint.h`` in ``jansson.h``
+
+
+Version 1.1.1
+=============
+
+Released 2009-10-26
+
+* All documentation files were not distributed with v1.1; build
+  documentation in make distcheck to prevent this in the future
+* Fix v1.1 release date in ``CHANGES``
+
+
+Version 1.1
+===========
+
+Released 2009-10-20
+
+* API additions and improvements:
+
+  - Extend array and object APIs
+  - Add functions to modify integer, real and string values
+  - Improve argument validation
+  - Use unsigned int instead of ``uint32_t`` for encoding flags
+
+* Enhance documentation
+
+  - Add getting started guide and tutorial
+  - Fix some typos
+  - General clarifications and cleanup
+
+* Check for integer and real overflows and underflows in decoder
+* Make singleton values thread-safe (``true``, ``false`` and ``null``)
+* Enhance circular reference handling
+* Don't define ``-std=c99`` in ``AM_CFLAGS``
+* Add C++ guards to ``jansson.h``
+* Minor performance and portability improvements
+* Expand test coverage
+
+
+Version 1.0.4
+=============
+
+Released 2009-10-11
+
+* Relax Autoconf version requirement to 2.59
+* Make Jansson compile on platforms where plain ``char`` is unsigned
+* Fix API tests for object
+
+
+Version 1.0.3
+=============
+
+Released 2009-09-14
+
+* Check for integer and real overflows and underflows in decoder
+* Use the Python json module for tests, or simplejson if the json
+  module is not found
+* Distribute changelog (this file)
+
+
+Version 1.0.2
+=============
+
+Released 2009-09-08
+
+* Handle EOF correctly in decoder
+
+
+Version 1.0.1
+=============
+
+Released 2009-09-04
+
+* Fixed broken `json_is_boolean()`
+
+
+Version 1.0
+===========
+
+Released 2009-08-25
+
+* Initial release

+ 684 - 0
compat/jansson-2.9/CMakeLists.txt

@@ -0,0 +1,684 @@
+# Notes:
+#
+# Author: Paul Harris, June 2012
+# Additions: Joakim Soderberg, Febuary 2013
+#
+# Supports: building static/shared, release/debug/etc, can also build html docs
+# and some of the tests.
+# Note that its designed for out-of-tree builds, so it will not pollute your
+# source tree.
+#
+# TODO 1: Finish implementing tests. api tests are working, but the valgrind
+# variants are not flagging problems.
+#
+# TODO 2: There is a check_exports script that would try and incorporate.
+#
+# TODO 3: Consolidate version numbers, currently the version number is written
+# into: * cmake (here) * autotools (the configure) * source code header files.
+# Should not be written directly into header files, autotools/cmake can do
+# that job.
+#
+# Brief intro on how to use cmake:
+# > mkdir build (somewhere - we do out-of-tree builds)
+# > use cmake, ccmake, or cmake-gui to configure the project. for linux, you
+# can only choose one variant: release,debug,etc... and static or shared.
+# >> example:
+# >> cd build
+# >> ccmake -i ../path_to_jansson_dir
+# >>  inside, configure your options. press C until there are no lines
+#     with * next to them.
+# >>  note, I like to configure the 'install' path to ../install, so I get
+#     self-contained clean installs I can point other projects to.
+# >>  press G to 'generate' the project files.
+# >> make (to build the project)
+# >> make install
+# >> make test (to run the tests, if you enabled them)
+#
+# Brief description on how it works:
+# There is a small heirachy of CMakeLists.txt files which define how the
+# project is built.
+# Header file detection etc is done, and the results are written into config.h
+# and jansson_config.h, which are generated from the corresponding
+# config.h.cmake and jansson_config.h.cmake template files.
+# The generated header files end up in the build directory - not in
+# the source directory.
+# The rest is down to the usual make process.
+
+
+
+cmake_minimum_required (VERSION 2.8)
+# required for exports? cmake_minimum_required (VERSION 2.8.6)
+project (jansson C)
+
+# Options
+option(JANSSON_BUILD_SHARED_LIBS "Build shared libraries." OFF)
+option(USE_URANDOM "Use /dev/urandom to seed the hash function." ON)
+option(USE_WINDOWS_CRYPTOAPI "Use CryptGenRandom to seed the hash function." ON)
+
+if (MSVC)
+   # This option must match the settings used in your program, in particular if you
+	# are linking statically
+	option(JANSSON_STATIC_CRT "Link the static CRT libraries" OFF )
+endif ()
+
+option(JANSSON_EXAMPLES "Compile example applications" ON)
+
+if (UNIX)
+   option(JANSSON_COVERAGE "(GCC Only! Requires gcov/lcov to be installed). Include target for doing coverage analysis for the test suite. Note that -DCMAKE_BUILD_TYPE=Debug must be set" OFF)
+   option(JANSSON_COVERALLS "Generate coverage info for Coveralls" OFF)
+   option(JANSSON_COVERALLS_UPLOAD "Upload coverage info to Coveralls (Only works via Travis)" ON)
+endif ()
+
+# Set some nicer output dirs.
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
+set(JANSSON_TEMP_DIR ${PROJECT_BINARY_DIR}/tmp)
+
+# Give the debug version a different postfix for windows,
+# so both the debug and release version can be built in the
+# same build-tree on Windows (MSVC).
+if (WIN32)
+   set(CMAKE_DEBUG_POSTFIX "_d")
+endif (WIN32)
+
+# This is how I thought it should go
+# set (JANSSON_VERSION "2.3.1")
+# set (JANSSON_SOVERSION 2)
+
+set(JANSSON_DISPLAY_VERSION "2.9")
+
+# This is what is required to match the same numbers as automake's
+set(JANSSON_VERSION "4.9.0")
+set(JANSSON_SOVERSION 4)
+
+# for CheckFunctionKeywords
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+include (CheckCSourceCompiles)
+include (CheckFunctionExists)
+include (CheckFunctionKeywords)
+include (CheckIncludeFiles)
+include (CheckTypeSize)
+
+if (MSVC)
+   # Turn off Microsofts "security" warnings.
+   add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4005 /wd4996 /nologo" )
+   
+   if (JANSSON_STATIC_CRT)
+      set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT")
+      set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd")
+   endif()
+   
+endif()
+
+if (NOT WIN32 AND (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX))
+   add_definitions("-fPIC")
+endif()
+
+message("C compiler: ${CMAKE_C_COMPILER_ID}")
+
+# Coverage only works with GCC for a debug build.
+if (JANSSON_COVERALLS)
+   set(JANSSON_COVERAGE ON)
+endif()
+
+if (JANSSON_COVERAGE)
+   include(CodeCoverage)
+   include(Coveralls)
+
+   # This adds coverage arguments to gcc/clang.
+   coveralls_turn_on_coverage()
+endif()
+
+check_include_files (endian.h HAVE_ENDIAN_H)
+check_include_files (fcntl.h HAVE_FCNTL_H)
+check_include_files (sched.h HAVE_SCHED_H)
+check_include_files (unistd.h HAVE_UNISTD_H)
+check_include_files (sys/param.h HAVE_SYS_PARAM_H)
+check_include_files (sys/stat.h HAVE_SYS_STAT_H)
+check_include_files (sys/time.h HAVE_SYS_TIME_H)
+check_include_files (sys/time.h HAVE_SYS_TYPES_H)
+
+check_function_exists (close HAVE_CLOSE)
+check_function_exists (getpid HAVE_GETPID)
+check_function_exists (gettimeofday HAVE_GETTIMEOFDAY)
+check_function_exists (open HAVE_OPEN)
+check_function_exists (read HAVE_READ)
+check_function_exists (sched_yield HAVE_SCHED_YIELD)
+
+# Check for the int-type includes
+check_include_files (stdint.h HAVE_STDINT_H)
+
+# Check our 64 bit integer sizes
+check_type_size (__int64 __INT64)
+check_type_size (int64_t INT64_T)
+check_type_size ("long long" LONG_LONG_INT)
+
+# Check our 32 bit integer sizes
+check_type_size (int32_t INT32_T)
+check_type_size (__int32 __INT32)
+check_type_size ("long" LONG_INT)
+check_type_size ("int" INT)
+if (HAVE_INT32_T)
+   set (JSON_INT32 int32_t)
+elseif (HAVE___INT32)
+   set (JSON_INT32 __int32)
+elseif (HAVE_LONG_INT AND (${LONG_INT} EQUAL 4))
+   set (JSON_INT32 long)
+elseif (HAVE_INT AND (${INT} EQUAL 4))
+   set (JSON_INT32 int)
+else ()
+   message (FATAL_ERROR "Could not detect a valid 32-bit integer type")
+endif ()
+
+check_type_size ("unsigned long" UNSIGNED_LONG_INT)
+check_type_size ("unsigned int" UNSIGNED_INT)
+check_type_size ("unsigned short" UNSIGNED_SHORT)
+
+check_type_size (uint32_t UINT32_T)
+check_type_size (__uint32 __UINT32)
+if (HAVE_UINT32_T)
+   set (JSON_UINT32 uint32_t)
+elseif (HAVE___UINT32)
+   set (JSON_UINT32 __uint32)
+elseif (HAVE_UNSIGNED_LONG_INT AND (${UNSIGNED_LONG_INT} EQUAL 4))
+   set (JSON_UINT32 "unsigned long")
+elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 4))
+   set (JSON_UINT32 "unsigned int")
+else ()
+   message (FATAL_ERROR "Could not detect a valid unsigned 32-bit integer type")
+endif ()
+
+check_type_size (uint16_t UINT16_T)
+check_type_size (__uint16 __UINT16)
+if (HAVE_UINT16_T)
+   set (JSON_UINT16 uint16_t)
+elseif (HAVE___UINT16)
+   set (JSON_UINT16 __uint16)
+elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 2))
+   set (JSON_UINT16 "unsigned int")
+elseif (HAVE_UNSIGNED_SHORT AND (${UNSIGNED_SHORT} EQUAL 2))
+   set (JSON_UINT16 "unsigned short")
+else ()
+   message (FATAL_ERROR "Could not detect a valid unsigned 16-bit integer type")
+endif ()
+
+check_type_size (uint8_t UINT8_T)
+check_type_size (__uint8 __UINT8)
+if (HAVE_UINT8_T)
+   set (JSON_UINT8 uint8_t)
+elseif (HAVE___UINT8)
+   set (JSON_UINT8 __uint8)
+else ()
+   set (JSON_UINT8 "unsigned char")
+endif ()
+
+# Check for ssize_t and SSIZE_T existance.
+check_type_size(ssize_t SSIZE_T)
+check_type_size(SSIZE_T UPPERCASE_SSIZE_T)
+if(NOT HAVE_SSIZE_T)
+   if(HAVE_UPPERCASE_SSIZE_T)
+      set(JSON_SSIZE SSIZE_T)
+   else()
+      set(JSON_SSIZE int)
+   endif()
+endif()
+set(CMAKE_EXTRA_INCLUDE_FILES "")
+
+# Check for all the variants of strtoll
+check_function_exists (strtoll HAVE_STRTOLL)
+check_function_exists (strtoq HAVE_STRTOQ)
+check_function_exists (_strtoi64 HAVE__STRTOI64)
+
+# Figure out what variant we should use
+if (HAVE_STRTOLL)
+   set (JSON_STRTOINT strtoll)
+elseif (HAVE_STRTOQ)
+   set (JSON_STRTOINT strtoq)
+elseif (HAVE__STRTOI64)
+   set (JSON_STRTOINT _strtoi64)
+else ()
+   # fallback to strtol (32 bit)
+   # this will set all the required variables
+   set (JSON_STRTOINT strtol)
+   set (JSON_INT_T long)
+   set (JSON_INTEGER_FORMAT "\"ld\"")
+endif ()
+
+# if we haven't defined JSON_INT_T, then we have a 64 bit conversion function.
+# detect what to use for the 64 bit type.
+# Note: I will prefer long long if I can get it, as that is what the automake system aimed for.
+if (NOT DEFINED JSON_INT_T)
+   if (HAVE_LONG_LONG_INT AND (${LONG_LONG_INT} EQUAL 8))
+      set (JSON_INT_T "long long")
+   elseif (HAVE_INT64_T)
+      set (JSON_INT_T int64_t)
+   elseif (HAVE___INT64)
+      set (JSON_INT_T __int64)
+   else ()
+      message (FATAL_ERROR "Could not detect 64 bit type, although I detected the strtoll equivalent")
+   endif ()
+
+   # Apparently, Borland BCC and MSVC wants I64d,
+   # Borland BCC could also accept LD
+   # and gcc wants ldd,
+   # I am not sure what cygwin will want, so I will assume I64d
+
+   if (WIN32) # matches both msvc and cygwin
+      set (JSON_INTEGER_FORMAT "\"I64d\"")
+   else ()
+      set (JSON_INTEGER_FORMAT "\"lld\"")
+   endif ()
+endif ()
+
+
+# If locale.h and localeconv() are available, define to 1, otherwise to 0.
+check_include_files (locale.h HAVE_LOCALE_H)
+check_function_exists (localeconv HAVE_LOCALECONV)
+
+if (HAVE_LOCALECONV AND HAVE_LOCALE_H)
+   set (JSON_HAVE_LOCALECONV 1)
+else ()
+   set (JSON_HAVE_LOCALECONV 0)
+endif()
+
+# check if we have setlocale
+check_function_exists(setlocale HAVE_SETLOCALE)
+
+# Check what the inline keyword is.
+# Note that the original JSON_INLINE was always set to just 'inline', so this goes further.
+check_function_keywords("inline")
+check_function_keywords("__inline")
+check_function_keywords("__inline__")
+
+if (HAVE_INLINE)
+   set(JSON_INLINE inline)
+elseif (HAVE___INLINE)
+   set(JSON_INLINE __inline)
+elseif (HAVE___INLINE__)
+   set(JSON_INLINE __inline__)
+else()
+   # no inline on this platform
+   set (JSON_INLINE)
+endif()
+
+check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); return 0; } " HAVE_SYNC_BUILTINS)
+check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); return 0; }" HAVE_ATOMIC_BUILTINS)
+
+set (JANSSON_INITIAL_HASHTABLE_ORDER 3 CACHE STRING "Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.")
+
+# configure the public config file
+configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake
+                ${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h)
+
+# Copy the jansson.h file to the public include folder
+file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h
+           DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/)
+
+add_definitions(-DJANSSON_USING_CMAKE)
+
+# configure the private config file
+configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_private_config.h.cmake
+                ${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h)
+
+# and tell the source code to include it
+add_definitions(-DHAVE_CONFIG_H)
+
+include_directories (${CMAKE_CURRENT_BINARY_DIR}/include)
+include_directories (${CMAKE_CURRENT_BINARY_DIR}/private_include)
+
+# Add the lib sources.
+file(GLOB JANSSON_SRC src/*.c)
+
+set(JANSSON_HDR_PRIVATE
+   ${CMAKE_CURRENT_SOURCE_DIR}/src/hashtable.h
+   ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson_private.h
+   ${CMAKE_CURRENT_SOURCE_DIR}/src/strbuffer.h
+   ${CMAKE_CURRENT_SOURCE_DIR}/src/utf.h
+   ${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h)
+
+set(JANSSON_HDR_PUBLIC 
+   ${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h
+   ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h)
+
+source_group("Library Sources" FILES ${JANSSON_SRC})
+source_group("Library Private Headers" FILES ${JANSSON_HDR_PRIVATE})
+source_group("Library Public Headers" FILES ${JANSSON_HDR_PUBLIC})
+
+if(JANSSON_BUILD_SHARED_LIBS)
+   add_library(jansson SHARED 
+      ${JANSSON_SRC} 
+      ${JANSSON_HDR_PRIVATE} 
+      ${JANSSON_HDR_PUBLIC} 
+      src/jansson.def)
+
+   set_target_properties(jansson PROPERTIES
+      VERSION ${JANSSON_VERSION}
+      SOVERSION ${JANSSON_SOVERSION})
+else()
+   add_library(jansson 
+      ${JANSSON_SRC}
+      ${JANSSON_HDR_PRIVATE} 
+      ${JANSSON_HDR_PUBLIC})
+endif()
+
+if (JANSSON_EXAMPLES)
+	add_executable(simple_parse "${PROJECT_SOURCE_DIR}/examples/simple_parse.c")
+	target_link_libraries(simple_parse jansson)
+endif()
+
+# For building Documentation (uses Sphinx)
+option(JANSSON_BUILD_DOCS "Build documentation (uses python-sphinx)." ON)
+if (JANSSON_BUILD_DOCS)
+   find_package(Sphinx)
+
+   if (NOT SPHINX_FOUND)
+      message(WARNING "Sphinx not found. Cannot generate documentation! 
+      Set -DJANSSON_BUILD_DOCS=OFF to get rid of this message.")
+   else()
+      if (Sphinx_VERSION_STRING VERSION_LESS 1.0)
+         message(WARNING "Your Sphinx version is too old! 
+               This project requires Sphinx v1.0 or above to produce 
+               proper documentation (you have v${Sphinx_VERSION_STRING}).
+               You will get output but it will have errors.")
+      endif()
+
+      # configured documentation tools and intermediate build results
+      set(BINARY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/_build")
+
+      # Sphinx cache with pickled ReST documents
+      set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees")
+
+      # CMake could be used to build the conf.py file too,
+      # eg it could automatically write the version of the program or change the theme.
+      # if(NOT DEFINED SPHINX_THEME)
+      #    set(SPHINX_THEME default)
+      # endif()
+      #
+      # if(NOT DEFINED SPHINX_THEME_DIR)
+      #    set(SPHINX_THEME_DIR)
+      # endif()
+      #
+      # configure_file(
+      #    "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in"
+      #    "${BINARY_BUILD_DIR}/conf.py"
+      #    @ONLY)
+
+      # TODO: Add support for all sphinx builders: http://sphinx-doc.org/builders.html
+
+      # Add documentation targets.
+      set(DOC_TARGETS html)
+
+      option(JANSSON_BUILD_MAN "Create a target for building man pages." ON)
+
+      if (JANSSON_BUILD_MAN)
+         if (Sphinx_VERSION_STRING VERSION_LESS 1.0)
+            message(WARNING "Sphinx version 1.0 > is required to build man pages. You have v${Sphinx_VERSION_STRING}.")
+         else()
+            list(APPEND DOC_TARGETS man)
+         endif()
+      endif()
+
+      option(JANSSON_BUILD_LATEX "Create a target for building latex docs (to create PDF)." OFF)
+
+      if (JANSSON_BUILD_LATEX)
+         find_package(LATEX)
+
+         if (NOT LATEX_COMPILER)
+            message("Couldn't find Latex, can't build latex docs using Sphinx")
+         else()
+            message("Latex found! If you have problems building, see Sphinx documentation for required Latex packages.")
+            list(APPEND DOC_TARGETS latex)
+         endif()
+      endif()
+      
+      # The doc target will build all documentation targets.
+      add_custom_target(doc)
+
+      foreach (DOC_TARGET ${DOC_TARGETS})
+         add_custom_target(${DOC_TARGET}
+            ${SPHINX_EXECUTABLE}
+            # -q   # Enable for quiet mode
+            -b ${DOC_TARGET}
+            -d "${SPHINX_CACHE_DIR}"
+            # -c "${BINARY_BUILD_DIR}" # enable if using cmake-generated conf.py
+            "${CMAKE_CURRENT_SOURCE_DIR}/doc"
+            "${CMAKE_CURRENT_BINARY_DIR}/doc/${DOC_TARGET}"
+            COMMENT "Building ${DOC_TARGET} documentation with Sphinx")
+
+         add_dependencies(doc ${DOC_TARGET})
+      endforeach()
+
+      message("Building documentation enabled for: ${DOC_TARGETS}")
+   endif()
+endif ()
+
+
+option(JANSSON_WITHOUT_TESTS "Don't build tests ('make test' to execute tests)" OFF)
+
+if (NOT JANSSON_WITHOUT_TESTS)
+   option(JANSSON_TEST_WITH_VALGRIND "Enable valgrind tests." OFF)
+
+   ENABLE_TESTING()
+
+   if (JANSSON_TEST_WITH_VALGRIND)
+      # TODO: Add FindValgrind.cmake instead of having a hardcoded path.
+
+      add_definitions(-DVALGRIND)
+
+      # enable valgrind
+      set(CMAKE_MEMORYCHECK_COMMAND valgrind)
+      set(CMAKE_MEMORYCHECK_COMMAND_OPTIONS
+         "--error-exitcode=1 --leak-check=full --show-reachable=yes --track-origins=yes -q")
+
+      set(MEMCHECK_COMMAND
+         "${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS}")
+      separate_arguments(MEMCHECK_COMMAND)
+   endif ()
+
+   #
+   # Test suites.
+   #
+   if (CMAKE_COMPILER_IS_GNUCC)
+      add_definitions(-Wall -Wextra -Wdeclaration-after-statement)
+   endif ()
+
+   set(api_tests
+         test_array
+         test_copy
+         test_dump
+         test_dump_callback
+         test_equal
+         test_load
+         test_loadb
+         test_number
+         test_object
+         test_pack
+         test_simple
+         test_unpack)
+
+   # Doing arithmetic on void pointers is not allowed by Microsofts compiler
+   # such as secure_malloc and secure_free is doing, so exclude it for now.
+   if (NOT MSVC)
+      list(APPEND api_tests test_memory_funcs)
+   endif()
+
+   # Helper macro for building and linking a test program.
+   macro(build_testprog name dir)
+       add_executable(${name} ${dir}/${name}.c)
+       add_dependencies(${name} jansson)
+       target_link_libraries(${name} jansson)
+   endmacro(build_testprog)
+
+   # Create executables and tests/valgrind tests for API tests.
+   foreach (test ${api_tests})
+      build_testprog(${test} ${PROJECT_SOURCE_DIR}/test/suites/api)
+
+      if (JANSSON_TEST_WITH_VALGRIND)
+         add_test(memcheck__${test}
+             ${MEMCHECK_COMMAND} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
+             WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
+      else()
+         add_test(${test}
+             ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
+             WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
+      endif ()
+   endforeach ()
+
+   # Test harness for the suites tests.
+   build_testprog(json_process ${PROJECT_SOURCE_DIR}/test/bin)
+
+   set(SUITE_TEST_CMD ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process)
+   set(SUITES encoding-flags valid invalid invalid-unicode)
+   foreach (SUITE ${SUITES})
+       file(GLOB TESTDIRS ${jansson_SOURCE_DIR}/test/suites/${SUITE}/*)
+
+       foreach (TESTDIR ${TESTDIRS})
+         if (IS_DIRECTORY ${TESTDIR})
+            get_filename_component(TNAME ${TESTDIR} NAME)
+
+            if (JANSSON_TEST_WITH_VALGRIND)
+               add_test(memcheck__${SUITE}__${TNAME}
+                        ${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} ${TESTDIR})
+            else()
+               add_test(${SUITE}__${TNAME}
+                        ${SUITE_TEST_CMD} ${TESTDIR})
+            endif()
+
+            if ((${SUITE} STREQUAL "valid" OR ${SUITE} STREQUAL "invalid") AND NOT EXISTS ${TESTDIR}/nostrip)
+               if (JANSSON_TEST_WITH_VALGRIND)
+                  add_test(memcheck__${SUITE}__${TNAME}__strip
+                           ${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} --strip ${TESTDIR})
+               else()
+                  add_test(${SUITE}__${TNAME}__strip
+                           ${SUITE_TEST_CMD} --strip ${TESTDIR})
+               endif()
+            endif ()
+         endif ()
+       endforeach ()
+   endforeach ()
+
+   if (JANSSON_COVERAGE)
+      setup_target_for_coverage(
+            coverage             # Coverage make target "make coverage".
+            coverage             # Name of output directory.
+            make                 # Name of test runner executable.
+            test)                # Arguments to the test runner above (make test).
+
+      if (JANSSON_COVERALLS)
+         set(COVERAGE_SRCS ${JANSSON_SRC})
+         coveralls_setup("${COVERAGE_SRCS}" ${JANSSON_COVERALLS_UPLOAD})
+      endif ()
+   endif ()
+
+   # Enable using "make check" just like the autotools project.
+   # By default cmake creates a target "make test"
+   add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} 
+                     DEPENDS json_process ${api_tests})
+endif ()
+
+#
+# Installation preparation.
+#
+
+# Allow the user to override installation directories.
+set(JANSSON_INSTALL_LIB_DIR       lib CACHE PATH "Installation directory for libraries")
+set(JANSSON_INSTALL_BIN_DIR       bin CACHE PATH "Installation directory for executables")
+set(JANSSON_INSTALL_INCLUDE_DIR   include CACHE PATH "Installation directory for header files")
+
+if(WIN32 AND NOT CYGWIN)
+  set(DEF_INSTALL_CMAKE_DIR cmake)
+else()
+  set(DEF_INSTALL_CMAKE_DIR lib/cmake/jansson)
+endif()
+
+set(JANSSON_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files")
+
+# Create pkg-conf file.
+# (We use the same files as ./configure does, so we
+#  have to defined the same variables used there).
+set(prefix      ${CMAKE_INSTALL_PREFIX})
+set(exec_prefix ${CMAKE_INSTALL_PREFIX})
+set(libdir      ${CMAKE_INSTALL_PREFIX}/${JANSSON_INSTALL_LIB_DIR})
+set(VERSION     ${JANSSON_DISPLAY_VERSION})
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jansson.pc.in
+               ${CMAKE_CURRENT_BINARY_DIR}/jansson.pc @ONLY)
+
+# Make sure the paths are absolute.
+foreach(p LIB BIN INCLUDE CMAKE)
+    set(var JANSSON_INSTALL_${p}_DIR)
+    if(NOT IS_ABSOLUTE "${${var}}")
+        set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}")
+    endif()
+endforeach()
+
+# Export targets (This is used for other CMake projects to easily find the libraries and include files).
+export(TARGETS jansson
+        FILE "${PROJECT_BINARY_DIR}/JanssonTargets.cmake")
+export(PACKAGE jansson)
+
+# Generate the config file for the build-tree.
+set(JANSSON__INCLUDE_DIRS 
+    "${PROJECT_SOURCE_DIR}/include"
+    "${PROJECT_BINARY_DIR}/include")
+set(JANSSON_INCLUDE_DIRS ${JANSSON__INCLUDE_DIRS} CACHE PATH "Jansson include directories")
+configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
+                ${PROJECT_BINARY_DIR}/JanssonConfig.cmake 
+                @ONLY)
+
+# Generate the config file for the installation tree.
+file(RELATIVE_PATH 
+    REL_INCLUDE_DIR 
+    "${JANSSON_INSTALL_CMAKE_DIR}"
+    "${JANSSON_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the Cmake dir.
+
+# Note the EVENT_CMAKE_DIR is defined in JanssonConfig.cmake.in, 
+# we escape it here so it's evaluated when it is included instead
+# so that the include dirs are given relative to where the 
+# config file is located.
+set(JANSSON__INCLUDE_DIRS 
+    "\${JANSSON_CMAKE_DIR}/${REL_INCLUDE_DIR}") 
+configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
+                ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/JanssonConfig.cmake 
+                @ONLY)
+
+# Generate version info for both build-tree and install-tree.
+configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfigVersion.cmake.in
+                ${PROJECT_BINARY_DIR}/JanssonConfigVersion.cmake 
+                @ONLY)
+
+# Define the public headers.
+set_target_properties(jansson PROPERTIES PUBLIC_HEADER "${JANSSON_HDR_PUBLIC}")
+#TODO: fix this.
+
+#
+# Install targets.
+#
+option(JANSSON_INSTALL "Generate installation target" ON)
+if (JANSSON_INSTALL)
+install(TARGETS jansson
+        EXPORT JanssonTargets
+        LIBRARY DESTINATION "${JANSSON_INSTALL_LIB_DIR}" COMPONENT lib
+        ARCHIVE DESTINATION "${JANSSON_INSTALL_LIB_DIR}" COMPONENT lib
+        RUNTIME DESTINATION "${JANSSON_INSTALL_BIN_DIR}" COMPONENT lib # Windows DLLs
+        PUBLIC_HEADER DESTINATION "${JANSSON_INSTALL_INCLUDE_DIR}" COMPONENT dev)
+
+# Install the pkg-config.
+install (FILES 
+         ${CMAKE_CURRENT_BINARY_DIR}/jansson.pc
+         DESTINATION ${JANSSON_INSTALL_LIB_DIR}/pkgconfig COMPONENT dev)
+
+# Install the configs.
+install(FILES
+    ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/JanssonConfig.cmake
+    ${PROJECT_BINARY_DIR}/JanssonConfigVersion.cmake
+    DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" COMPONENT dev)
+
+# Install exports for the install-tree.
+install(EXPORT JanssonTargets 
+        DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" COMPONENT dev)
+endif()
+
+# For use when simply using add_library from a parent project to build jansson.
+set(JANSSON_LIBRARIES jansson CACHE STRING "Jansson libraries")

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