Browse Source

Merge pull request #1 from luke-jr/bfgminer

Merge upstream
jstefanop 8 years ago
parent
commit
6382db1d68
18 changed files with 346 additions and 139 deletions
  1. 30 0
      NEWS
  2. 11 13
      README.OpenWrt
  3. 9 5
      api.c
  4. 19 8
      configure.ac
  5. 14 0
      debian/changelog
  6. 1 1
      debian/control
  7. 2 0
      driver-antminer.c
  8. 1 2
      driver-avalonmm.c
  9. 1 1
      driver-bitmain.c
  10. 3 3
      driver-icarus.c
  11. 132 62
      driver-stratum.c
  12. 1 1
      libblkmaker
  13. 70 30
      miner.c
  14. 2 1
      miner.h
  15. 14 2
      openwrt/bfgminer/Makefile
  16. 17 6
      openwrt/multibuild.sh
  17. 18 4
      work2d.c
  18. 1 0
      work2d.h

+ 30 - 0
NEWS

@@ -1,3 +1,33 @@
+BFGMiner Version 5.4.2 - March 26, 2016
+
+- Update official Win32/64 build compiler and libraries:
+- - Win64 compiler (GCC) from 4.7.4 to 5.3.0 (Win32 remains at 4.7.4)
+- - libcurl from 7.39.0 to 7.47.1
+- - libevent from 2.0.21 to 2.1.5-r4
+- - libusb from 1.0.18 (Win32) and 1.0.19 (Win64) to 1.0.20
+- - libmicrohttpd from 0.9.38_pre33603 to 0.9.48
+- Update libblkmaker to 0.5.3
+- Bugfix: Sanitise lock situation for work2d
+- Bugfix: SSM: Never issue the same work2d twice in a row
+- Bugfix: SSM: Never send mining.notify for stale work2d
+- SSM: Refactor work2d check and job pruning from _stratumsrv_update_notify
+into stratumsrv_update_notify_str
+- Updates for OpenWrt 15.05
+- Bugfix: Correct work2d handling for GBT servers
+
+
+BFGMiner Version 5.4.1 - November 21, 2015
+
+- bitmain: Use more portable PRIu64 rather than %llu (which fails on Windows)
+- icarus: Use more portable PRIu64 rather than %llu (which fails on Windows)
+- Bugfix: openwrt: Fix bitmain option
+- Bugfix: antminer: Avoid strstr(NULL, ...) when device has no [available]
+product string
+- Update libblkmaker to 0.5.2
+- openwrt: Add option to enable bitmain driver
+- openwrt: Update libusb device list
+
+
 BFGMiner Version 5.4.0 - October 23, 2015
 
 - AUTHORS: Move jstefanop to current maintainers

+ 11 - 13
README.OpenWrt

@@ -1,12 +1,14 @@
-Open up /etc/opkg.conf (on your router) in your favourite editor. You will see
-a line that looks similar to this at the top (depending on your device):
-	src/gz attitude_adjustment http://downloads.openwrt.org/attitude_adjustment/12.09/ar71xx/generic/packages
-Note the platform following the OpenWrt version. In this example, it is ar71xx.
+First, look at /etc/openwrt_release (on your router). You will see a bunch of
+attributes for the OpenWrt system. The DISTRIB_RELEASE attribute is your
+OpenWrt version, and the first part of DISTRIB_TARGET (before the slash) is
+the router's platform.
 
-Now add a new line immediately below it, similar to this:
-	src/gz bfgminer http://luke.dashjr.org/programs/bitcoin/files/bfgminer/latest/openwrt/12.09/ar71xx
-Be sure you put the same platform at the end as your OpenWrt repository!
-Also note that you can change "latest" to "stable" or "testing" to get better-tested versions.
+Now, open up /etc/opkg.conf (again, on your router) in your favourite editor.
+Add a new line at the bottom, similar to this:
+	src/gz bfgminer http://luke.dashjr.org/programs/bitcoin/files/bfgminer/latest/openwrt/15.05/ar71xx
+In this example, 15.05 is the version of OpenWrt, and ar71xx is the platform.
+Be sure to change those to match your router! You can also change "latest" to
+"stable" or "testing" to get better-tested versions.
 
 Next, save the file and exit your editor. Tell opkg to reload its package lists
 by running the command:
@@ -16,12 +18,8 @@ you open an issue for your router's platform, it may be possible to add
 support.
 
 If all went well updating your package list, you can now install BFGMiner and
-any drivers and/or bitstreams you might need:
+any drivers you might need:
 	opkg install bfgminer
 	opkg install kmod-usb-serial-ftdi
 	opkg install kmod-usb-serial-cp210x
 	opkg install kmod-usb-serial-pl2303
-	opkg find bitstream*
-	opkg install bitstream-ztex-ufm1_15y1
-	opkg install bitstream-ztex-ufm1_15b1
-	opkg install bitstream-fpgaminer

+ 9 - 5
api.c

@@ -313,6 +313,7 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_SETQUOTA 122
 
 #define MSG_INVSTRATEGY 0x102
+#define MSG_FAILPORT 0x103
 
 #define USE_ALTMSG 0x4000
 
@@ -466,6 +467,7 @@ struct CODES {
  { 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_ERR,   MSG_INVSTRATEGY,	PARAM_STR,	"Invalid strategy for '%s'" },
+ { SEVERITY_ERR,   MSG_FAILPORT,	PARAM_BOTH,	"Failed to set port (%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'" },
@@ -3203,12 +3205,12 @@ static void debugstate(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha
 		io_close(io_data);
 }
 
-extern void stratumsrv_change_port();
+extern bool stratumsrv_change_port(unsigned);
 
 static void setconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 	char *comma;
-	int value;
+	long value;
 
 	if (param == NULL || *param == '\0') {
 		message(io_data, MSG_CONPAR, 0, NULL, isjson);
@@ -3251,7 +3253,7 @@ static void setconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 		return;
 	}
 
-	value = atoi(comma);
+	value = atol(comma);
 	if (value < 0 || value > 9999) {
 		message(io_data, MSG_INVNUM, value, param, isjson);
 		return;
@@ -3275,8 +3277,10 @@ static void setconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 #ifdef USE_LIBEVENT
 	else if (strcasecmp(param, "stratum-port") == 0)
 	{
-		stratumsrv_port = value;
-		stratumsrv_change_port();
+		if (!stratumsrv_change_port(value)) {
+			message(io_data, MSG_FAILPORT, value, param, isjson);
+			return;
+		}
 	}
 #endif
 	else {

+ 19 - 8
configure.ac

@@ -15,7 +15,7 @@ dnl * any later version.  See COPYING for more details.
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 m4_define([v_maj], [5])
 m4_define([v_min], [4])
-m4_define([v_mic], [0])
+m4_define([v_mic], [2])
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 m4_define([v_ver], [v_maj.v_min.v_mic])
 m4_define([lt_rev], m4_eval(v_maj + v_min))
@@ -223,7 +223,7 @@ m4_define([BFG_FIND_INCLUDE_PATH],[
 	m4_pushdef([_result_var],[inclpath_[]patsubst(_header_file,[[./]],[_])])
 	AC_CHECK_HEADER([_rel_path/_header_file],[
 		AC_MSG_CHECKING([_header_file path])
-		_result_var=`echo '[#]include <'"_rel_path"'/_header_file>' | ${CPP} -M - 2>/dev/null | ${SED} [-E -e ':a' -e '/\\$/!b b' -e N -e 's/\\\n/ /' -e 't a' -e ':b' -e 's/^[^:]*:[[:space:]]*(([^[:space:]\]|\\.)*[[:space:]])*(([^[:space:]\]|\\.)*)]patsubst([_header_file],[\.],[\\.])[([[:space:]].*)?$/\3/' -e 't' -e d]`
+		_result_var=`echo '[#]include <'"_rel_path"'/_header_file>' | ${CPP} -M - 2>/dev/null | ${SED} [-E -e ':a' -e '/\\$/!b b' -e N -e 's/\\\n/ /' -e 't a' -e ':b' -e 's/^[^:]*:[[:space:]]*(([^[:space:]\]|\\.)*[[:space:]])*(([^[:space:]\]|\\.)*)(\\|\\\\|\/)?]patsubst([_header_file],[\.],[\\.])[([[:space:]].*)?$/\3/' -e 't' -e d]`
 		if test "x$_result_var" = "x"; then
 			AC_MSG_RESULT([failed])
 			AC_MSG_ERROR([Couldn't determine include path for _header_file])
@@ -661,10 +661,23 @@ AC_ARG_WITH([libevent],
 )
 if test "x$libevent" != "xno"; then
 	PKG_CHECK_MODULES([libevent],[libevent >= 2.0.3],[
-		AC_DEFINE([USE_LIBEVENT],[1],[Defined to 1 if libevent support is wanted])
+		PKG_CHECK_MODULES([libevent_pthreads],[libevent_pthreads],[
+			libevent_CFLAGS="${libevent_CFLAGS} ${libevent_pthreads_CFLAGS}"
+			libevent_LIBS="${libevent_LIBS} ${libevent_pthreads_LIBS}"
+		],[true])
+		AC_MSG_CHECKING([if libevent supports threading])
+		BFG_PREPROC_IFELSE([EVTHREAD_USE_PTHREADS_IMPLEMENTED || EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED],[event2/thread.h],[
+			AC_MSG_RESULT([yes])
+			have_libevent=yes
+		],[
+			AC_MSG_RESULT([no])
+		])
+	])
+	if test "x$have_libevent" = "xyes"; then
 		libevent=yes
-	],[
-		libevent=no
+		need_work2d=yes
+		AC_DEFINE([USE_LIBEVENT],[1],[Defined to 1 if libevent support is wanted])
+	else
 		libevent_enableaction="install libevent 2.0.3+"
 		if test -n "$need_bfg_driver_proxy_enableaction"; then
 			need_bfg_driver_proxy_enableaction="${need_bfg_driver_proxy_enableaction} (getwork) or libevent 2.0.3+ (stratum)"
@@ -676,9 +689,7 @@ if test "x$libevent" != "xno"; then
 		else
 			AC_MSG_WARN([libevent 2.0.3+ not found; stratum proxy will be unavailable])
 		fi
-	])
-	if test "x$libevent" = "xyes"; then
-		need_work2d=yes
+		libevent=no
 	fi
 fi
 AM_CONDITIONAL([USE_LIBEVENT], [test x$libevent = xyes])

+ 14 - 0
debian/changelog

@@ -1,3 +1,17 @@
+bfgminer (5.4.2-0precise1) precise; urgency=low
+
+  * Fix some possible edge case problems with stratum proxying (--stratum-port)
+  * openwrt: Binary packages for 15.05
+  * Fix 2D work support for GBT servers (requires libblkmaker 0.5.3 or newer)
+
+ -- Luke Dashjr <luke+bfgminer@dashjr.org>  Sat, 26 Mar 2016 06:52:15 -0000
+
+bfgminer (5.4.1-0precise1) precise; urgency=low
+
+  * Bugfixes only.
+
+ -- Luke Dashjr <luke+bfgminer@dashjr.org>  Sat, 21 Nov 2015 18:12:26 -0000
+
 bfgminer (5.4.0-0precise1) precise; urgency=low
 
   * RPC: Ability to change pool management strategy.

+ 1 - 1
debian/control

@@ -2,7 +2,7 @@ Source: bfgminer
 Priority: optional
 Section: misc
 Maintainer: Luke Dashjr <luke_bfgminer@dashjr.org>
-Standards-Version: 5.4.0
+Standards-Version: 5.4.2
 Build-Depends: build-essential, debhelper, autoconf, automake, libtool, libssl-dev, yasm, pkg-config, libudev-dev, libcurl4-openssl-dev, wget, unzip, libjansson-dev, libncurses5-dev, libudev-dev, libusb-1.0-0-dev, git, quilt, uthash-dev, libsensors4-dev
 
 Package: bfgminer

+ 2 - 0
driver-antminer.c

@@ -50,6 +50,8 @@ static const char *bm1382_chips[] = {
 
 static bool antminer_chip_has_bm1382_freq_register(const char * const prodstr)
 {
+	if (!prodstr)
+		return false;
 	for (const char **chipname = bm1382_chips; *chipname; ++chipname) {
 		if (strstr(prodstr, *chipname)) {
 			return true;

+ 1 - 2
driver-avalonmm.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Luke Dashjr
+ * Copyright 2014-2016 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
@@ -509,7 +509,6 @@ bool avalonmm_update_swork_from_pool(struct cgpu_info * const master_dev, struct
 	stratum_work_cpy(&mmjob->swork, &pool->swork);
 	cg_runlock(&pool->data_lock);
 	timer_set_now(&mmjob->tv_prepared);
-	mmjob->swork.data_lock_p = NULL;
 	if (!avalonmm_send_swork(fd, chain, &mmjob->swork, mmjob->jobid, &mmjob->nonce_diff))
 	{
 		avalonmm_free_job(mmjob);

+ 1 - 1
driver-bitmain.c

@@ -671,7 +671,7 @@ static int bitmain_read(struct cgpu_info *bitmain, unsigned char *buf,
 	size_t total = 0;
 
 	if(bitmain == NULL || buf == NULL || bufsize <= 0) {
-		applog(LOG_WARNING, "bitmain_read parameter error bufsize(%llu)", (unsigned long long)bufsize);
+		applog(LOG_WARNING, "bitmain_read parameter error bufsize(%"PRIu64")", (uint64_t)bufsize);
 		return -1;
 	}
 	{

+ 3 - 3
driver-icarus.c

@@ -756,7 +756,7 @@ const struct cgpu_info *icarus_proc_for_nonce(const struct cgpu_info * const ica
 	struct ICARUS_INFO * const info = icarus->device_data;
 	unsigned proc_id = 0;
 	for (int i = info->work_division, j = 0; i /= 2; ++j)
-		if (nonce & (1 << (31 - j)))
+		if (nonce & (1UL << (31 - j)))
 			proc_id |= (1 << j);
 	const struct cgpu_info * const proc = device_proc_by_id(icarus, proc_id) ?: icarus;
 	return proc;
@@ -1102,12 +1102,12 @@ keepwaiting:
 		{
 			const uint64_t elapsed_fs = (elapsed.tv_sec * 1000000000000000LL) + (elapsed.tv_usec * 1000000000LL);
 			const uint64_t est_Hs_fs = elapsed_fs / hash_count;
-			applog(LOG_DEBUG, "%"PRIpreprv" nonce = 0x%08x = 0x%08" PRIx64 " hashes (%"PRId64".%06lus; %llu.%06luns/hash)",
+			applog(LOG_DEBUG, "%"PRIpreprv" nonce = 0x%08x = 0x%08" PRIx64 " hashes (%"PRId64".%06lus; %"PRIu64".%06luns/hash)",
 			       proc->proc_repr,
 			       nonce,
 			       (uint64_t)hash_count,
 			       (int64_t)elapsed.tv_sec, (unsigned long)elapsed.tv_usec,
-			       (unsigned long long)(est_Hs_fs / 1000000LL), (unsigned long)(est_Hs_fs % 1000000LL));
+			       (uint64_t)(est_Hs_fs / 1000000LL), (unsigned long)(est_Hs_fs % 1000000LL));
 		}
 	}
 	else

+ 132 - 62
driver-stratum.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2013-2014 Luke Dashjr
+ * Copyright 2013-2016 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
@@ -24,6 +24,7 @@
 #include <event2/bufferevent.h>
 #include <event2/event.h>
 #include <event2/listener.h>
+#include <event2/thread.h>
 
 #include <jansson.h>
 
@@ -101,8 +102,6 @@ void stratumsrv_send_set_difficulty(struct stratumsrv_conn * const conn, const f
 	bufferevent_write(bev, buf, bufsz);
 }
 
-#define _ssm_gen_dummy_work work2d_gen_dummy_work
-
 static
 float stratumsrv_choose_share_pdiff(const struct stratumsrv_conn * const conn, const struct mining_algorithm * const malgo)
 {
@@ -112,27 +111,75 @@ float stratumsrv_choose_share_pdiff(const struct stratumsrv_conn * const conn, c
 	return conn_pdiff;
 }
 
+static void stratumsrv_boot_all_subscribed(const char *);
+static void _ssj_free(struct stratumsrv_job *);
+static void stratumsrv_job_pruner();
+
 static
-bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
+bool stratumsrv_update_notify_str(struct pool * const pool)
 {
+	const bool clean = _ssm_cur_job_work.pool ? stale_work(&_ssm_cur_job_work, true) : true;
+	struct timeval tv_now;
+	
 	cg_rlock(&pool->data_lock);
 	
+	if (!pool_has_usable_swork(pool))
+	{
+fail:
+		cg_runlock(&pool->data_lock);
+		applog(LOG_WARNING, "SSM: No usable 2D work upstream!");
+		if (clean)
+			stratumsrv_boot_all_subscribed("Current upstream pool does not have usable 2D work");
+		return false;
+	}
+	
+	timer_set_now(&tv_now);
+	
+	{
+		struct work work;
+		work2d_gen_dummy_work_for_stale_check(&work, &pool->swork, &tv_now, NULL);
+		
+		const bool is_stale = stale_work(&work, false);
+		
+		clean_work(&work);
+		
+		if (is_stale) {
+			cg_runlock(&pool->data_lock);
+			applog(LOG_DEBUG, "SSM: Ignoring work update notification while pool %d has stale swork", pool->pool_no);
+			return false;
+		}
+	}
+	
 	struct stratumsrv_conn *conn;
 	const struct stratum_work * const swork = &pool->swork;
 	const int n2size = pool->swork.n2size;
+	const size_t coinb2_offset = swork->nonce2_offset + n2size;
+	const size_t coinb2_len = bytes_len(&swork->coinbase) - swork->nonce2_offset - n2size;
+	
+	if (_ssm_last_ssj &&
+	    !(memcmp(&swork->header1[0], &_ssm_last_ssj->swork.header1[0], 0x24)
+	   || swork->nonce2_offset != _ssm_last_ssj->swork.nonce2_offset
+	   || bytes_len(&swork->coinbase) != bytes_len(&_ssm_last_ssj->swork.coinbase)
+	   || memcmp(bytes_buf(&swork->coinbase), bytes_buf(&_ssm_last_ssj->swork.coinbase), swork->nonce2_offset)
+	   || memcmp(&bytes_buf(&swork->coinbase)[coinb2_offset], &bytes_buf(&_ssm_last_ssj->swork.coinbase)[coinb2_offset], coinb2_len)
+	   || memcmp(swork->diffbits, _ssm_last_ssj->swork.diffbits, 4)
+	)) {
+		cg_runlock(&pool->data_lock);
+		applog(LOG_DEBUG, "SSM: Updating with (near?-)identical work2d; skipping...");
+		return false;
+	}
+	
 	char my_job_id[33];
 	int i;
 	struct stratumsrv_job *ssj;
 	ssize_t n2pad = work2d_pad_xnonce_size(swork);
 	if (n2pad < 0)
 	{
-		cg_runlock(&pool->data_lock);
-		return false;
+		goto fail;
 	}
 	size_t coinb1in_lenx = swork->nonce2_offset * 2;
 	size_t n2padx = n2pad * 2;
 	size_t coinb1_lenx = coinb1in_lenx + n2padx;
-	size_t coinb2_len = bytes_len(&swork->coinbase) - swork->nonce2_offset - n2size;
 	size_t coinb2_lenx = coinb2_len * 2;
 	sprintf(my_job_id, "%"PRIx64"-%"PRIx64, (uint64_t)time(NULL), _ssm_jobid++);
 	// NOTE: The buffer has up to 2 extra/unused bytes:
@@ -147,7 +194,7 @@ bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
 	bin2hex(coinb1, bytes_buf(&swork->coinbase), swork->nonce2_offset);
 	work2d_pad_xnonce(&coinb1[coinb1in_lenx], swork, true);
 	coinb1[coinb1_lenx] = '\0';
-	bin2hex(coinb2, &bytes_buf(&swork->coinbase)[swork->nonce2_offset + n2size], coinb2_len);
+	bin2hex(coinb2, &bytes_buf(&swork->coinbase)[coinb2_offset], coinb2_len);
 	p += sprintf(p, "{\"params\":[\"%s\",\"%s\",\"%s\",\"%s\",[", my_job_id, prevhash, coinb1, coinb2);
 	for (i = 0; i < swork->merkles; ++i)
 	{
@@ -172,17 +219,30 @@ bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
 	*ssj = (struct stratumsrv_job){
 		.my_job_id = strdup(my_job_id),
 	};
-	timer_set_now(&ssj->tv_prepared);
+	ssj->tv_prepared = tv_now;
 	stratum_work_cpy(&ssj->swork, swork);
 	
 	cg_runlock(&pool->data_lock);
 	
-	ssj->swork.data_lock_p = NULL;
+	if (clean)
+	{
+		struct stratumsrv_job *ssj, *tmp;
+		
+		applog(LOG_DEBUG, "SSM: Current replacing job stale, pruning all jobs");
+		HASH_ITER(hh, _ssm_jobs, ssj, tmp)
+		{
+			HASH_DEL(_ssm_jobs, ssj);
+			_ssj_free(ssj);
+		}
+	}
+	else
+		stratumsrv_job_pruner();
+	
 	HASH_ADD_KEYPTR(hh, _ssm_jobs, ssj->my_job_id, strlen(ssj->my_job_id), ssj);
 	
 	if (likely(_ssm_cur_job_work.pool))
 		clean_work(&_ssm_cur_job_work);
-	_ssm_gen_dummy_work(&_ssm_cur_job_work, &ssj->swork, &ssj->tv_prepared, NULL, 0);
+	work2d_gen_dummy_work_for_stale_check(&_ssm_cur_job_work, &ssj->swork, &ssj->tv_prepared, NULL);
 	
 	_ssm_notify_sz = p - buf;
 	assert(_ssm_notify_sz <= bufsz);
@@ -329,7 +389,6 @@ static
 void _stratumsrv_update_notify(evutil_socket_t fd, short what, __maybe_unused void *p)
 {
 	struct pool *pool = current_pool();
-	bool clean;
 	
 	if (fd == _ssm_update_notifier[0])
 	{
@@ -338,37 +397,8 @@ void _stratumsrv_update_notify(evutil_socket_t fd, short what, __maybe_unused vo
 		applog(LOG_DEBUG, "SSM: Update triggered by notifier");
 	}
 	
-	clean = _ssm_cur_job_work.pool ? stale_work(&_ssm_cur_job_work, true) : true;
-	if (clean)
-	{
-		struct stratumsrv_job *ssj, *tmp;
-		
-		applog(LOG_DEBUG, "SSM: Current replacing job stale, pruning all jobs");
-		HASH_ITER(hh, _ssm_jobs, ssj, tmp)
-		{
-			HASH_DEL(_ssm_jobs, ssj);
-			_ssj_free(ssj);
-		}
-	}
-	else
-		stratumsrv_job_pruner();
-	
-	if (!pool_has_usable_swork(pool))
-	{
-		applog(LOG_WARNING, "SSM: No usable 2D work upstream!");
-		if (clean)
-			stratumsrv_boot_all_subscribed("Current upstream pool does not have usable 2D work");
-		goto out;
-	}
-	
-	if (!stratumsrv_update_notify_str(pool, clean))
-	{
-		applog(LOG_WARNING, "SSM: Failed to subdivide upstream stratum notify!");
-		if (clean)
-			stratumsrv_boot_all_subscribed("Current upstream pool does not have active stratum");
-	}
+	stratumsrv_update_notify_str(pool);
 	
-out: ;
 	struct timeval tv_scantime = {
 		.tv_sec = opt_scantime,
 	};
@@ -789,32 +819,42 @@ void stratumlistener(struct evconnlistener *listener, evutil_socket_t sock, stru
 	bufferevent_enable(bev, EV_READ | EV_WRITE);
 }
 
-void stratumsrv_start();
+static bool stratumsrv_init_server(void);
 
-void stratumsrv_change_port()
+bool stratumsrv_change_port(const unsigned port)
 {
-	struct event_base * const evbase = _smm_evbase;
-	
-	if (_smm_listener)
-		evconnlistener_free(_smm_listener);
-	
-	if (!_smm_running)
-	{
-		stratumsrv_start();
-		return;
+	if (!_smm_running) {
+		if (!stratumsrv_init_server()) {
+			return false;
+		}
 	}
 	
+	struct event_base * const evbase = _smm_evbase;
+	struct evconnlistener * const old_smm_listener = _smm_listener;
+	
 	struct sockaddr_in sin = {
 		.sin_family = AF_INET,
 		.sin_addr.s_addr = INADDR_ANY,
-		.sin_port = htons(stratumsrv_port),
+		.sin_port = htons(port),
 	};
 	_smm_listener = evconnlistener_new_bind(evbase, stratumlistener, NULL, (
 		LEV_OPT_CLOSE_ON_FREE | LEV_OPT_CLOSE_ON_EXEC | LEV_OPT_REUSEABLE
 	), 0x10, (void*)&sin, sizeof(sin));
 	
+	if (!_smm_listener) {
+		applog(LOG_ERR, "SSM: Failed to listen on port %u", (unsigned)port);
+		return false;
+	}
+	
 	// NOTE: libevent doesn't seem to implement LEV_OPT_CLOSE_ON_EXEC for Windows, so we must do this ourselves
 	set_cloexec_socket(evconnlistener_get_fd(_smm_listener), true);
+	
+	if (old_smm_listener) {
+		evconnlistener_free(old_smm_listener);
+	}
+	stratumsrv_port = port;
+	
+	return true;
 }
 
 static
@@ -823,29 +863,59 @@ void *stratumsrv_thread(__maybe_unused void *p)
 	pthread_detach(pthread_self());
 	RenameThread("stratumsrv");
 	
+	struct event_base *evbase = _smm_evbase;
+	event_base_dispatch(evbase);
+	_smm_running = false;
+	
+	return NULL;
+}
+
+static
+bool stratumsrv_init_server() {
 	work2d_init();
 	
+	if (-1
+#if EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
+	 && evthread_use_windows_threads()
+#endif
+#if EVTHREAD_USE_PTHREADS_IMPLEMENTED
+	 && evthread_use_pthreads()
+#endif
+	) {
+		applog(LOG_ERR, "SSM: %s failed", "event_use_*threads");
+		return false;
+	}
+	
 	struct event_base *evbase = event_base_new();
+	if (!evbase) {
+		applog(LOG_ERR, "SSM: %s failed", "event_base_new");
+		return false;
+	}
 	_smm_evbase = evbase;
+	
 	{
 		ev_notify = evtimer_new(evbase, _stratumsrv_update_notify, NULL);
+		if (!ev_notify) {
+			applog(LOG_ERR, "SSM: %s failed", "evtimer_new");
+			return false;
+		}
 		_stratumsrv_update_notify(-1, 0, NULL);
 	}
 	{
 		notifier_init(_ssm_update_notifier);
 		struct event *ev_update_notifier = event_new(evbase, _ssm_update_notifier[0], EV_READ | EV_PERSIST, _stratumsrv_update_notify, NULL);
+		if (!ev_update_notifier) {
+			applog(LOG_ERR, "SSM: %s failed", "event_new");
+			return false;
+		}
 		event_add(ev_update_notifier, NULL);
 	}
-	stratumsrv_change_port();
-	event_base_dispatch(evbase);
 	
-	return NULL;
-}
-
-void stratumsrv_start()
-{
 	_smm_running = true;
+	
 	pthread_t pth;
 	if (unlikely(pthread_create(&pth, NULL, stratumsrv_thread, NULL)))
 		quit(1, "stratumsrv thread create failed");
-}
+	
+	return true;
+}

+ 1 - 1
libblkmaker

@@ -1 +1 @@
-Subproject commit e9cd82be9352d502fce9df696a8933567e1e00a4
+Subproject commit d2dde7a9edd4ec0360b82dd70be5586163ac2fe8

+ 70 - 30
miner.c

@@ -1,6 +1,6 @@
 /*
  * Copyright 2011-2014 Con Kolivas
- * Copyright 2011-2014 Luke Dashjr
+ * Copyright 2011-2016 Luke Dashjr
  * Copyright 2014 Nate Woolls
  * Copyright 2012-2014 Andrew Smith
  * Copyright 2010 Jeff Garzik
@@ -183,7 +183,7 @@ bool opt_restart = true;
 int httpsrv_port = -1;
 #endif
 #ifdef USE_LIBEVENT
-int stratumsrv_port = -1;
+long stratumsrv_port = -1;
 #endif
 
 const
@@ -1173,6 +1173,7 @@ struct pool *add_pool2(struct mining_goal_info * const goal)
 	if (unlikely(pthread_cond_init(&pool->cr_cond, bfg_condattr)))
 		quit(1, "Failed to pthread_cond_init in add_pool");
 	cglock_init(&pool->data_lock);
+	pool->swork.data_lock_p = &pool->data_lock;
 	mutex_init(&pool->stratum_lock);
 	timer_unset(&pool->swork.tv_transparency);
 	pool->swork.pool = pool;
@@ -1314,6 +1315,23 @@ static char *set_int_1_to_10(const char *arg, int *i)
 	return set_int_range(arg, i, 1, 10);
 }
 
+static char *set_long_1_to_65535_or_neg1(const char * const arg, long * const i)
+{
+	const long min = 1, max = 65535;
+	
+	char * const err = opt_set_longval(arg, i);
+	
+	if (err) {
+		return err;
+	}
+	
+	if (*i != -1 && (*i < min || *i > max)) {
+		return "Value out of range";
+	}
+	
+	return NULL;
+}
+
 char *set_strdup(const char *arg, char **p)
 {
 	*p = strdup((char *)arg);
@@ -2714,7 +2732,7 @@ static struct opt_table opt_config_table[] = {
 		     "Set socks proxy (host:port)"),
 #ifdef USE_LIBEVENT
 	OPT_WITH_ARG("--stratum-port",
-	             opt_set_intval, opt_show_intval, &stratumsrv_port,
+	             set_long_1_to_65535_or_neg1, opt_show_longval, &stratumsrv_port,
 	             "Port number to listen on for stratum miners (-1 means disabled)"),
 #endif
 	OPT_WITHOUT_ARG("--submit-stale",
@@ -3443,7 +3461,7 @@ void refresh_bitcoind_address(struct mining_goal_info * const goal, const bool f
 
 #define GBT_XNONCESZ (sizeof(uint32_t))
 
-#if BLKMAKER_VERSION > 4
+#if BLKMAKER_VERSION > 6
 #define blkmk_append_coinbase_safe(tmpl, append, appendsz)  \
        blkmk_append_coinbase_safe2(tmpl, append, appendsz, GBT_XNONCESZ, false)
 #endif
@@ -3624,7 +3642,7 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 
 	work->tv_staged = tv_now;
 	
-#if BLKMAKER_VERSION > 4
+#if BLKMAKER_VERSION > 6
 	if (work->tr)
 	{
 		blktemplate_t * const tmpl = work->tr->tmpl;
@@ -3644,15 +3662,18 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 			pool_check_coinbase(pool, cbtxn, cbtxnsz);
 			
 			cg_wlock(&pool->data_lock);
+			if (swork->tr)
+				tmpl_decref(swork->tr);
 			swork->tr = work->tr;
+			tmpl_incref(swork->tr);
 			bytes_assimilate_raw(&swork->coinbase, cbtxn, cbtxnsz, cbtxnsz);
 			swork->nonce2_offset = cbextranonceoffset;
 			bytes_assimilate_raw(&swork->merkle_bin, branches, branchdatasz, branchdatasz);
 			swork->merkles = branchcount;
-			memcpy(swork->header1, &buf[0], 36);
+			swap32yes(swork->header1, &buf[0], 36 / 4);
 			swork->ntime = le32toh(*(uint32_t *)(&buf[68]));
 			swork->tv_received = tv_now;
-			memcpy(swork->diffbits, &buf[72], 4);
+			swap32yes(swork->diffbits, &buf[72], 4 / 4);
 			memcpy(swork->target, work->target, sizeof(swork->target));
 			free(swork->job_id);
 			swork->job_id = NULL;
@@ -3666,7 +3687,7 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 		else
 			applog(LOG_DEBUG, "blkmk_get_mdata failed for pool %u", pool->pool_no);
 	}
-#endif  // BLKMAKER_VERSION > 4
+#endif  // BLKMAKER_VERSION > 6
 	pool_set_opaque(pool, !work->tr);
 
 	ret = true;
@@ -5346,6 +5367,11 @@ static char *submit_upstream_work_request(struct work *work)
 		unsigned char data[80];
 		
 		swap32yes(data, work->data, 80 / 4);
+#if BLKMAKER_VERSION > 6
+		if (work->stratum) {
+			req = blkmk_submitm_jansson(tmpl, data, bytes_buf(&work->nonce2), bytes_len(&work->nonce2), le32toh(*((uint32_t*)&work->data[76])), work->do_foreign_submit);
+		} else
+#endif
 #if BLKMAKER_VERSION > 3
 		if (work->do_foreign_submit)
 			req = blkmk_submit_foreign_jansson(tmpl, data, work->dataid, le32toh(*((uint32_t*)&work->data[76])));
@@ -6780,7 +6806,7 @@ static struct submit_work_state *begin_submission(struct work *work)
 		timer_set_delay_from_now(&sws->tv_staleexpire, 300000000);
 	}
 
-	if (work->stratum) {
+	if (work->getwork_mode == GETWORK_MODE_STRATUM) {
 		char *s;
 
 		s = malloc(1024);
@@ -7940,7 +7966,7 @@ void write_config(FILE *fcfg)
 #endif
 #ifdef USE_LIBEVENT
 	if (stratumsrv_port != -1)
-		fprintf(fcfg, ",\n\"stratum-port\" : %d", stratumsrv_port);
+		fprintf(fcfg, ",\n\"stratum-port\" : %ld", stratumsrv_port);
 #endif
 	_write_config_string_elist(fcfg, "device", opt_devices_enabled_list);
 	_write_config_string_elist(fcfg, "set-device", opt_set_device_list);
@@ -10264,6 +10290,7 @@ void stratum_work_cpy(struct stratum_work * const dst, const struct stratum_work
 	dst->job_id = maybe_strdup(src->job_id);
 	bytes_cpy(&dst->coinbase, &src->coinbase);
 	bytes_cpy(&dst->merkle_bin, &src->merkle_bin);
+	dst->data_lock_p = NULL;
 }
 
 void stratum_work_clean(struct stratum_work * const swork)
@@ -10298,7 +10325,6 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 	clean_work(work);
 	
 	cg_wlock(&pool->data_lock);
-	pool->swork.data_lock_p = &pool->data_lock;
 	
 	const int n2size = pool->swork.n2size;
 	bytes_resize(&work->nonce2, n2size);
@@ -10323,11 +10349,8 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 
 void gen_stratum_work2(struct work *work, struct stratum_work *swork)
 {
-	unsigned char *coinbase, merkle_root[32], merkle_sha[64];
-	uint8_t *merkle_bin;
-	uint32_t *data32, *swap32;
-	int i;
-
+	unsigned char *coinbase;
+	
 	/* Generate coinbase */
 	coinbase = bytes_buf(&swork->coinbase);
 	memcpy(&coinbase[swork->nonce2_offset], bytes_buf(&work->nonce2), bytes_len(&work->nonce2));
@@ -10335,7 +10358,29 @@ void gen_stratum_work2(struct work *work, struct stratum_work *swork)
 	/* Downgrade to a read lock to read off the variables */
 	if (swork->data_lock_p)
 		cg_dwlock(swork->data_lock_p);
+	
+	gen_stratum_work3(work, swork, swork->data_lock_p);
+	
+	if (opt_debug)
+	{
+		char header[161];
+		char nonce2hex[(bytes_len(&work->nonce2) * 2) + 1];
+		bin2hex(header, work->data, 80);
+		bin2hex(nonce2hex, bytes_buf(&work->nonce2), bytes_len(&work->nonce2));
+		applog(LOG_DEBUG, "Generated stratum header %s", header);
+		applog(LOG_DEBUG, "Work job_id %s nonce2 %s", work->job_id, nonce2hex);
+	}
+}
 
+void gen_stratum_work3(struct work * const work, struct stratum_work * const swork, cglock_t * const data_lock_p)
+{
+	unsigned char *coinbase, merkle_root[32], merkle_sha[64];
+	uint8_t *merkle_bin;
+	uint32_t *data32, *swap32;
+	int i;
+	
+	coinbase = bytes_buf(&swork->coinbase);
+	
 	/* Generate merkle root */
 	gen_hash(coinbase, merkle_root, bytes_len(&swork->coinbase));
 	memcpy(merkle_sha, merkle_root, 32);
@@ -10362,18 +10407,8 @@ void gen_stratum_work2(struct work *work, struct stratum_work *swork)
 	memcpy(work->target, swork->target, sizeof(work->target));
 	work->job_id = maybe_strdup(swork->job_id);
 	work->nonce1 = maybe_strdup(swork->nonce1);
-	if (swork->data_lock_p)
-		cg_runlock(swork->data_lock_p);
-
-	if (opt_debug)
-	{
-		char header[161];
-		char nonce2hex[(bytes_len(&work->nonce2) * 2) + 1];
-		bin2hex(header, work->data, 80);
-		bin2hex(nonce2hex, bytes_buf(&work->nonce2), bytes_len(&work->nonce2));
-		applog(LOG_DEBUG, "Generated stratum header %s", header);
-		applog(LOG_DEBUG, "Work job_id %s nonce2 %s", work->job_id, nonce2hex);
-	}
+	if (data_lock_p)
+		cg_runlock(data_lock_p);
 
 	calc_midstate(work);
 
@@ -10383,6 +10418,11 @@ void gen_stratum_work2(struct work *work, struct stratum_work *swork)
 	work->id = total_work++;
 	work->longpoll = false;
 	work->getwork_mode = GETWORK_MODE_STRATUM;
+	if (swork->tr) {
+		work->getwork_mode = GETWORK_MODE_GBT;
+		work->tr = swork->tr;
+		tmpl_incref(work->tr);
+	}
 	calc_diff(work, 0);
 }
 
@@ -13185,7 +13225,7 @@ void bfg_atexit(void)
 }
 
 extern void bfg_init_threadlocal();
-extern void stratumsrv_start();
+extern bool stratumsrv_change_port(unsigned);
 extern void test_aan_pll(void);
 
 int main(int argc, char *argv[])
@@ -13748,7 +13788,7 @@ begin_bench:
 
 #ifdef USE_LIBEVENT
 	if (stratumsrv_port != -1)
-		stratumsrv_start();
+		stratumsrv_change_port(stratumsrv_port);
 #endif
 
 #ifdef HAVE_BFG_HOTPLUG

+ 2 - 1
miner.h

@@ -985,7 +985,7 @@ extern int last_logstatusline_len;
 extern bool have_libusb;
 #endif
 extern int httpsrv_port;
-extern int stratumsrv_port;
+extern long stratumsrv_port;
 extern char *opt_api_allow;
 extern bool opt_api_mcast;
 extern char *opt_api_mcast_addr;
@@ -1543,6 +1543,7 @@ extern void stratum_work_cpy(struct stratum_work *dst, const struct stratum_work
 extern void stratum_work_clean(struct stratum_work *);
 extern bool pool_has_usable_swork(const struct pool *);
 extern void gen_stratum_work2(struct work *, struct stratum_work *);
+extern void gen_stratum_work3(struct work *, struct stratum_work *, cglock_t *data_lock_p);
 extern void inc_hw_errors3(struct thr_info *thr, const struct work *work, const uint32_t *bad_nonce_p, float nonce_diff);
 static inline
 void inc_hw_errors2(struct thr_info * const thr, const struct work * const work, const uint32_t *bad_nonce_p)

+ 14 - 2
openwrt/bfgminer/Makefile

@@ -11,12 +11,16 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=bfgminer
 PKG_TITLE:=BFGMiner
-PKG_VERSION:=5.4.0
+PKG_VERSION:=5.4.2
 PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).txz
 PKG_SOURCE_URL:=http://luke.dashjr.org/programs/bitcoin/files/$(PKG_NAME)/$(PKG_VERSION)/
 
+PKG_MAINTAINER:=Luke Dashjr <luke-jr+bfgminer@utopios.org>
+PKG_LICENSE:=GPL-3.0+
+PKG_LICENSE_FILES:=COPYING
+
 PKG_INSTALL:=1
 
 include $(INCLUDE_DIR)/package.mk
@@ -59,9 +63,13 @@ config PACKAGE_$(PKG_NAME)_libmicrohttpd
 	depends on PACKAGE_$(PKG_NAME)
 	default y
 config PACKAGE_$(PKG_NAME)_libusb
-	bool "Build with libusb support (X6500 & ZTEX)"
+	bool "Build with libusb support (Cointerra, HashBuster Micro, Klondike, X6500 & ZTEX)"
 	depends on PACKAGE_$(PKG_NAME)
 	default y
+config PACKAGE_$(PKG_NAME)_bitmain
+	bool "Build with bitmain driver (only for S* series miners)"
+	depends on PACKAGE_$(PKG_NAME)
+	default n
 config PACKAGE_$(PKG_NAME)_keccak
 	bool "Build with Keccak algorithm support"
 	depends on PACKAGE_$(PKG_NAME)
@@ -94,6 +102,10 @@ ifndef CONFIG_PACKAGE_$(PKG_NAME)_libusb
 CONFIGURE_ARGS += --without-libusb
 endif
 
+ifdef CONFIG_PACKAGE_$(PKG_NAME)_bitmain
+CONFIGURE_ARGS += --enable-bitmain
+endif
+
 ifdef CONFIG_PACKAGE_$(PKG_NAME)_keccak
 CONFIGURE_ARGS += --enable-keccak
 endif

+ 17 - 6
openwrt/multibuild.sh

@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright 2013 Luke Dashjr
+# Copyright 2013-2016 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
@@ -9,10 +9,13 @@
 set -e
 set -x
 reporoot="$1"  # .../files/bfgminer/BFGMINER_VERSION/openwrt/OPENWRT_VERSION
+openwrt_root="${2:-openwrt-src}"
+BITSTREAM_PKG_PATH="${3}"  # Relative to reporoot
 test -n "$reporoot"
 reporoot="$(realpath "$reporoot")"
 test -n "$reporoot"
-cd "openwrt-src/"
+cd "${openwrt_root}/"
+openwrt_root="$PWD"
 test -d "$reporoot"
 vcfgdir='vanilla_configs'
 vcfglist="$(
@@ -20,7 +23,6 @@ vcfglist="$(
 	 perl -ple 's[.*/][]' |
 	 sort -n
 )"
-BITSTREAM_PKG_PATH='../../../../bitstreams/openwrt/'  # Relative to reporoot
 BITSTREAMS=(
 	fpgaminer_402-1
 	ztex-ufm1_15b1_121126-1
@@ -52,15 +54,24 @@ for cfn in $vcfglist; do
 	yes '' | make oldconfig
 	make {tools,toolchain}/install package/bfgminer/{clean,compile}
 	mkdir "$reporoot/$plat" -pv
-	cp -v "bin/$plat/packages/"bfgminer*_${plat}.ipk "$reporoot/$plat/"
-	if [ -d "$reporoot/${BITSTREAM_PKG_PATH}" ]; then
+	files=$(ls "bin/$plat/packages/"{*/,}bfgminer*_${plat}*.ipk || true)
+	if test -z "${files}"; then
+		echo "Cannot find built packages"
+		exit 1
+	fi
+	cp -v ${files} "$reporoot/$plat/"
+	if [ -n "${BITSTREAM_PKG_PATH}" ]; then
 	(
+		test -d "$reporoot/${BITSTREAM_PKG_PATH}"
 		cd "$reporoot/$plat"
 		for bs in ${BITSTREAMS[@]}; do
 			ln -vfs "../${BITSTREAM_PKG_PATH}/bitstream-${bs}_all.ipk" .
 		done
 	)
 	fi
-	staging_dir/host/bin/ipkg-make-index "$reporoot/$plat/" > "$reporoot/$plat/Packages"
+	(
+		cd "$reporoot/$plat/"
+		"${openwrt_root}/scripts/ipkg-make-index.sh" .
+	) > "$reporoot/$plat/Packages"
 	gzip -9 < "$reporoot/$plat/Packages" > "$reporoot/$plat/Packages.gz"
 done

+ 18 - 4
work2d.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2013-2014 Luke Dashjr
+ * Copyright 2013-2016 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
@@ -9,6 +9,7 @@
 
 #include "config.h"
 
+#include <limits.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
@@ -70,15 +71,21 @@ void *work2d_pad_xnonce(void * const buf_, const struct stratum_work * const swo
 	return &buf[pad];
 }
 
-void work2d_gen_dummy_work(struct work * const work, struct stratum_work * const swork, const struct timeval * const tvp_prepared, const void * const xnonce2, const uint32_t xnonce1)
+static void work2d_gen_dummy_work_prepare(struct work * const work, struct stratum_work * const swork, const struct timeval * const tvp_prepared)
 {
-	uint8_t *p, *s;
-	
 	*work = (struct work){
 		.pool = swork->pool,
 		.work_restart_id = swork->work_restart_id,
 		.tv_staged = *tvp_prepared,
 	};
+}
+
+void work2d_gen_dummy_work(struct work * const work, struct stratum_work * const swork, const struct timeval * const tvp_prepared, const void * const xnonce2, const uint32_t xnonce1)
+{
+	uint8_t *p, *s;
+	
+	work2d_gen_dummy_work_prepare(work, swork, tvp_prepared);
+	
 	bytes_resize(&work->nonce2, swork->n2size);
 	s = bytes_buf(&work->nonce2);
 	p = &s[swork->n2size - work2d_xnonce2sz];
@@ -94,6 +101,12 @@ void work2d_gen_dummy_work(struct work * const work, struct stratum_work * const
 	gen_stratum_work2(work, swork);
 }
 
+void work2d_gen_dummy_work_for_stale_check(struct work * const work, struct stratum_work * const swork, const struct timeval * const tvp_prepared, cglock_t * const data_lock_p)
+{
+	work2d_gen_dummy_work_prepare(work, swork, tvp_prepared);
+	gen_stratum_work3(work, swork, data_lock_p);
+}
+
 bool work2d_submit_nonce(struct thr_info * const thr, struct stratum_work * const swork, const struct timeval * const tvp_prepared, const void * const xnonce2, const uint32_t xnonce1, const uint32_t nonce, const uint32_t ntime, bool * const out_is_stale, const float nonce_diff)
 {
 	struct work _work, *work;
@@ -104,6 +117,7 @@ bool work2d_submit_nonce(struct thr_info * const thr, struct stratum_work * cons
 	work2d_gen_dummy_work(work, swork, tvp_prepared, xnonce2, xnonce1);
 	*(uint32_t *)&work->data[68] = htobe32(ntime);
 	work->nonce_diff = nonce_diff;
+	work->rolltime = INT_MAX;  // FIXME
 	
 	// Check if it's stale, if desired
 	if (out_is_stale)

+ 1 - 0
work2d.h

@@ -16,6 +16,7 @@ extern void release_work2d_(uint32_t xnonce1);
 extern int work2d_pad_xnonce_size(const struct stratum_work *);
 extern void *work2d_pad_xnonce(void *buf, const struct stratum_work *, bool hex);
 extern void work2d_gen_dummy_work(struct work *, struct stratum_work *, const struct timeval *tvp_prepared, const void *xnonce2, uint32_t xnonce1);
+extern void work2d_gen_dummy_work_for_stale_check(struct work *, struct stratum_work *, const struct timeval *tvp_prepared, cglock_t *data_lock_p);
 extern bool work2d_submit_nonce(struct thr_info *, struct stratum_work *, const struct timeval *tvp_prepared, const void *xnonce2, uint32_t xnonce1, uint32_t nonce, uint32_t ntime, bool *out_is_stale, float nonce_diff);
 
 #endif