Browse Source

Merge branch 'cgmerges' into bfgminer

Luke Dashjr 13 years ago
parent
commit
a2be74a931
18 changed files with 537 additions and 128 deletions
  1. 41 2
      API-README
  2. 29 0
      NEWS
  3. 22 1
      README
  4. 78 2
      api.c
  5. 1 1
      compat.h
  6. 57 5
      driver-bitforce.c
  7. 1 1
      driver-cairnsmore.c
  8. 2 2
      driver-cpu.c
  9. 8 4
      driver-icarus.c
  10. 2 2
      driver-modminer.c
  11. 1 1
      driver-ztex.c
  12. 26 31
      fpgautils.c
  13. 17 15
      fpgautils.h
  14. 1 1
      libztex.c
  15. 174 50
      miner.c
  16. 37 3
      miner.h
  17. 34 7
      miner.php
  18. 6 0
      util.c

+ 41 - 2
API-README

@@ -263,6 +263,22 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               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 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 PGA devices will return a warning
+                              status message stating that they dont support it
+                              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
@@ -297,14 +313,16 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               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, Normal
+                              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|
+                              PerDevice=true/false,
+                              WorkTime=true/false|
 
  setconfig|name,N (*)
                none           There is no reply section just the STATUS section
@@ -365,6 +383,27 @@ miner.php - an example web page to access the API
 Feature Changelog for external applications using the API:
 
 
+API V1.19b
+
+Added API commands:
+ 'pgaidentify|N' (only works for BFL Singles so far)
+
+Modified API commands:
+ Change pool field name back from 'Diff1 Work' to 'Diff1 Shares'
+ 'devs' - add 'Difficulty Accepted', 'Difficulty Rejected',
+              'Last Share Difficulty' to all devices
+ 'gpu|N' - add 'Difficulty Accepted', 'Difficulty Rejected',
+              'Last Share Difficulty'
+ 'pga|N' - add 'Difficulty Accepted', 'Difficulty Rejected',
+              'Last Share Difficulty'
+ 'notify' - add '*Dev Throttle' (for BFL Singles)
+ 'pools' - add 'Difficulty Accepted', 'Difficulty Rejected',
+               'Difficulty Stale', 'Last Share Difficulty'
+ 'stats' - add 'Work Diff', 'Min Diff', 'Max Diff', 'Min Diff Count',
+               'Max Diff Count' to the pool stats
+
+----------
+
 API V1.19 (BFGMiner v2.8.0)
 
 Added API commands:

+ 29 - 0
NEWS

@@ -1,3 +1,32 @@
+BFGMiner Version 2.8.1 - Future
+
+- 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
+- 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
+- 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
+- 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
+
+
 BFGMiner Version 2.8.0 - September 15, 2012
 
 - Be specific about jansson version requirement

+ 22 - 1
README

@@ -246,6 +246,7 @@ The official supplied binaries are compiled with support for all FPGAs.
 To force the code to only attempt detection with a specific driver,
 prepend the argument with the driver name followed by a colon.
 For example, "icarus:/dev/ttyUSB0" or "bitforce:\\.\COM5"
+or using the short name: "ica:/dev/ttyUSB0" or "bfl:\\.\COM5"
 
 For other FPGA details see the FPGA-README
 
@@ -480,6 +481,26 @@ it will log to a file called logfile.txt and otherwise work the same.
 There is also the -m option on linux which will spawn a command of your choice
 and pipe the output directly to that command.
 
+The WorkTime details 'debug' option adds details on the end of each line
+displayed for Accepted or Rejected work done. An example would be:
+
+ <-00000059.ed4834a3 M:X D:1.0 G:17:02:38:0.405 C:1.855 (2.995) W:3.440 (0.000) S:0.461 R:17:02:47
+
+The first 2 hex codes are the previous block hash, the rest are reported in
+seconds unless stated otherwise:
+The previous hash is followed by the getwork mode used M:X where X is one of
+P:Pool, T:Test Pool, L:LP or B:Benchmark,
+then D:d.ddd is the difficulty required to get a share from the work,
+then G:hh:mm:ss:n.nnn, which is when the getwork or LP was sent to the pool and
+the n.nnn is how long it took to reply,
+followed by 'O' on it's own if it is an original getwork, or 'C:n.nnn' if it was
+a clone with n.nnn stating how long after the work was recieved that it was cloned,
+(m.mmm) is how long from when the original work was received until work started,
+W:n.nnn is how long the work took to process until it was ready to submit,
+(m.mmm) is how long from ready to submit to actually doing the submit, this is
+usually 0.000 unless there was a problem with submitting the work,
+S:n.nnn is how long it took to submit the completed work and await the reply,
+R:hh:mm:ss is the actual time the work submit reply was received
 
 If you start BFGMiner with the --sharelog option, you can get detailed
 information for each share found. The argument to the option may be "-" for
@@ -895,7 +916,7 @@ will give output like:
  0 crw-rw---- 1 root dialout 188, 0 2012-09-11 13:49 /dev/ttyUSB0
 This means your account must have the group 'dialout' or root priviledges
 To permanently give your account the 'dialout' group:
- sudo usermod `whoami` -a -G dialout
+ sudo usermod -G dialout -a `whoami`
 Then logout and back in again
 
 

+ 78 - 2
api.c

@@ -396,6 +396,8 @@ static const char *JSON_PARAMETER = "parameter";
 #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
@@ -557,7 +559,10 @@ struct CODES {
  { SEVERITY_SUCC,  MSG_FOO,	PARAM_BOOL,	"Failover-Only set to %s" },
  { SEVERITY_SUCC,  MSG_MINECOIN,PARAM_NONE,	"BFGMiner coin" },
  { SEVERITY_SUCC,  MSG_DEBUGSET,PARAM_STR,	"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_INVNUM,	PARAM_BOTH,	"Invalid number (%d) for '%s' range is 0-9999" },
@@ -568,7 +573,9 @@ struct CODES {
 
 static int my_thr_id = 0;
 static bool bye;
+#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
 static bool ping = true;
+#endif
 
 // Used to control quit restart access to shutdown variables
 static pthread_mutex_t quit_restart_lock;
@@ -774,6 +781,7 @@ static struct api_data *api_add_data_full(struct api_data *root, char *name, enu
 			case API_UTILITY:
 			case API_FREQ:
 			case API_HS:
+			case API_DIFF:
 				api_data->data = (void *)malloc(sizeof(double));
 				*((double *)(api_data->data)) = *((double *)data);
 				break;
@@ -904,6 +912,11 @@ struct api_data *api_add_hs(struct api_data *root, char *name, double *data, boo
 	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_json(struct api_data *root, char *name, json_t *data, bool copy_data)
 {
 	return api_add_data_full(root, name, API_JSON, (void *)data, copy_data);
@@ -980,6 +993,9 @@ static struct api_data *print_data(struct api_data *root, char *buf, bool isjson
 			case API_HS:
 				sprintf(buf, "%.15f", *((double *)(root->data)));
 				break;
+			case API_DIFF:
+				sprintf(buf, "%.8f", *((double *)(root->data)));
+				break;
 			case API_BOOL:
 				sprintf(buf, "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR);
 				break;
@@ -1409,6 +1425,9 @@ static void devstatus_an(struct cgpu_info *cgpu, bool isjson)
 	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_int(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);
 
 	if (cgpu->api->get_api_extra_device_status)
 		root = api_add_extra(root, cgpu->api->get_api_extra_device_status(cgpu));
@@ -1648,6 +1667,44 @@ static void pgadisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __
 
 	strcpy(io_buffer, message(MSG_PGADIS, id, NULL, isjson));
 }
+
+static void pgaidentify(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	int numpga = numpgas();
+	int id;
+
+	if (numpga == 0) {
+		strcpy(io_buffer, message(MSG_PGANON, 0, NULL, isjson));
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		strcpy(io_buffer, message(MSG_MISID, 0, NULL, isjson));
+		return;
+	}
+
+	id = atoi(param);
+	if (id < 0 || id >= numpga) {
+		strcpy(io_buffer, message(MSG_INVPGA, id, NULL, isjson));
+		return;
+	}
+
+	int dev = pgadevice(id);
+	if (dev < 0) { // Should never happen
+		strcpy(io_buffer, message(MSG_INVPGA, id, NULL, isjson));
+		return;
+	}
+
+	struct cgpu_info *cgpu = devices[dev];
+	struct device_api *api = cgpu->api;
+
+	if (!api->identify_device)
+		strcpy(io_buffer, message(MSG_PGANOID, id, NULL, isjson));
+	else {
+		api->identify_device(cgpu);
+		strcpy(io_buffer, message(MSG_PGAIDENT, id, NULL, isjson));
+	}
+}
 #endif
 
 #ifdef WANT_CPUMINE
@@ -1744,12 +1801,16 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
 		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_int(root, "Diff1 Work", &(pool->diff1), false);
+		root = api_add_int(root, "Diff1 Shares", &(pool->diff1), false);
 		if (pool->rpc_proxy) {
 			root = api_add_escape(root, "Proxy", pool->rpc_proxy, false);
 		} else {
 			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);
 
 		if (isjson && (i > 0))
 			strcat(io_buffer, COMMA);
@@ -1803,6 +1864,9 @@ static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo
 	root = api_add_uint(root, "Network Blocks", &(new_blocks), false);
 	root = api_add_mhtotal(root, "Total MH", &(total_mhashes_done), false);
 	root = api_add_utility(root, "Work Utility", &(work_utility), false);
+	root = api_add_diff(root, "Difficulty Accepted", &(total_diff_accepted), false);
+	root = api_add_diff(root, "Difficulty Rejected", &(total_diff_rejected), false);
+	root = api_add_diff(root, "Difficulty Stale", &(total_diff_stale), false);
 
 	root = print_data(root, buf, isjson);
 	if (isjson)
@@ -2515,6 +2579,7 @@ void notifystatus(int device, struct cgpu_info *cgpu, bool isjson, __maybe_unuse
 	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);
 
 	if (isjson && (device > 0))
 		strcat(io_buffer, COMMA);
@@ -2642,6 +2707,11 @@ static int itemstats(int i, char *id, struct cgminer_stats *stats, struct cgmine
 		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);
 	}
 
 	if (extra)
@@ -2795,6 +2865,10 @@ static void debugstate(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __
 		opt_quiet = false;
 		opt_protocol = false;
 		want_per_device_stats = false;
+		opt_worktime = false;
+		break;
+	case 'w':
+		opt_worktime ^= true;
 		break;
 	default:
 		// anything else just reports the settings
@@ -2812,6 +2886,7 @@ static void debugstate(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __
 	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(root, buf, isjson);
 	if (isjson)
@@ -2879,6 +2954,7 @@ struct CMDS {
 	{ "pga",		pgadev,		false },
 	{ "pgaenable",		pgaenable,	true },
 	{ "pgadisable",		pgadisable,	true },
+	{ "pgaidentify",	pgaidentify,	true },
 #endif
 #ifdef WANT_CPUMINE
 	{ "cpu",		cpudev,		false },

+ 1 - 1
compat.h

@@ -57,7 +57,7 @@ enum {
 	PRIO_PROCESS		= 0,
 };
 
-static inline int setpriority(int which, int who, int prio)
+static inline int setpriority(__maybe_unused int which, __maybe_unused int who, __maybe_unused int prio)
 {
 	return -!SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
 }

+ 57 - 5
driver-bitforce.c

@@ -19,8 +19,8 @@
 #include "config.h"
 
 #include "compat.h"
-#include "fpgautils.h"
 #include "miner.h"
+#include "fpgautils.h"
 
 #define BITFORCE_SLEEP_MS 500
 #define BITFORCE_TIMEOUT_S 7
@@ -76,6 +76,7 @@ static bool bitforce_detect_one(const char *devpath)
 	}
 
 	BFwrite(fdDev, "ZGX", 3);
+	pdevbuf[0] = '\0';
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	if (unlikely(!pdevbuf[0])) {
 		applog(LOG_DEBUG, "BFL: Error reading/timeout (ZGX)");
@@ -125,7 +126,7 @@ static int bitforce_detect_auto(void)
 
 static void bitforce_detect(void)
 {
-	serial_detect_auto(bitforce_api.dname, bitforce_detect_one, bitforce_detect_auto);
+	serial_detect_auto(&bitforce_api, bitforce_detect_one, bitforce_detect_auto);
 }
 
 static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
@@ -205,6 +206,7 @@ void bitforce_init(struct cgpu_info *bitforce)
 
 	do {
 		BFwrite(fdDev, "ZGX", 3);
+		pdevbuf[0] = '\0';
 		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 
 		if (unlikely(!pdevbuf[0])) {
@@ -234,6 +236,37 @@ void bitforce_init(struct cgpu_info *bitforce)
 	mutex_unlock(&bitforce->device_mutex);
 }
 
+static void bitforce_flash_led(struct cgpu_info *bitforce)
+{
+	int fdDev = bitforce->device_fd;
+
+	if (!fdDev)
+		return;
+
+	/* Do not try to flash the led if we're polling for a result to
+	 * minimise the chance of interleaved results */
+	if (bitforce->polling)
+		return;
+
+	/* It is not critical flashing the led so don't get stuck if we
+	 * can't grab the mutex here */
+	if (mutex_trylock(&bitforce->device_mutex))
+		return;
+
+	BFwrite(fdDev, "ZMX", 3);
+
+	/* Once we've tried - don't do it until told to again */
+	bitforce->flash_led = false;
+
+	/* However, this stops anything else getting a reply
+	 * So best to delay any other access to the BFL */
+	sleep(4);
+
+	mutex_unlock(&bitforce->device_mutex);
+
+	return; // nothing is returned by the BFL
+}
+
 static bool bitforce_get_temp(struct cgpu_info *bitforce)
 {
 	int fdDev = bitforce->device_fd;
@@ -244,16 +277,23 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 		return false;
 
 	/* Do not try to get the temperature if we're polling for a result to
-	 * minimise the change of interleaved results */
+	 * minimise the chance of interleaved results */
 	if (bitforce->polling)
 		return true;
 
-	/* It is not critical getting temperature so don't get stuck if  we
+	// Flash instead of Temp - doing both can be too slow
+	if (bitforce->flash_led) {
+		bitforce_flash_led(bitforce);
+ 		return true;
+	}
+
+	/* It is not critical getting temperature so don't get stuck if we
 	 * can't grab the mutex here */
 	if (mutex_trylock(&bitforce->device_mutex))
 		return false;
 
 	BFwrite(fdDev, "ZLX", 3);
+	pdevbuf[0] = '\0';
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	mutex_unlock(&bitforce->device_mutex);
 	
@@ -282,10 +322,13 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 		 * our responses are out of sync and flush the buffer to
 		 * hopefully recover */
 		applog(LOG_WARNING, "BFL%i: Garbled response probably throttling, clearing buffer", bitforce->device_id);
+		bitforce->device_last_not_well = time(NULL);
+		bitforce->device_not_well_reason = REASON_DEV_THROTTLE;
+		bitforce->dev_throttle_count++;
 		/* Count throttling episodes as hardware errors */
 		bitforce->hw_errors++;
 		bitforce_clear_buffer(bitforce);
-		return false;;
+		return false;
 	}
 
 	return true;
@@ -307,6 +350,7 @@ re_send:
 		BFwrite(fdDev, "ZPX", 3);
 	else
 		BFwrite(fdDev, "ZDX", 3);
+	pdevbuf[0] = '\0';
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	if (!pdevbuf[0] || !strncasecmp(pdevbuf, "B", 1)) {
 		mutex_unlock(&bitforce->device_mutex);
@@ -347,6 +391,7 @@ re_send:
 		BFwrite(fdDev, ob, 68);
 	}
 
+	pdevbuf[0] = '\0';
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	mutex_unlock(&bitforce->device_mutex);
 
@@ -397,6 +442,7 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 	while (1) {
 		mutex_lock(&bitforce->device_mutex);
 		BFwrite(fdDev, "ZFX", 3);
+		pdevbuf[0] = '\0';
 		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 		mutex_unlock(&bitforce->device_mutex);
 
@@ -554,6 +600,11 @@ static bool bitforce_get_stats(struct cgpu_info *bitforce)
 	return bitforce_get_temp(bitforce);
 }
 
+static void bitforce_identify(struct cgpu_info *bitforce)
+{
+	bitforce->flash_led = true;
+}
+
 static bool bitforce_thread_init(struct thr_info *thr)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
@@ -590,6 +641,7 @@ struct device_api bitforce_api = {
 	.reinit_device = bitforce_init,
 	.get_statline_before = get_bitforce_statline_before,
 	.get_stats = bitforce_get_stats,
+	.identify_device = bitforce_identify,
 	.thread_prepare = bitforce_thread_prepare,
 	.thread_init = bitforce_thread_init,
 	.scanhash = bitforce_scanhash,

+ 1 - 1
driver-cairnsmore.c

@@ -51,7 +51,7 @@ static int cairnsmore_detect_auto(void)
 static void cairnsmore_detect()
 {
 	// Actual serial detection is handled by Icarus driver
-	serial_detect_auto_byname(cairnsmore_api.dname, cairnsmore_detect_one, cairnsmore_detect_auto);
+	serial_detect_auto_byname(&cairnsmore_api, cairnsmore_detect_one, cairnsmore_detect_auto);
 }
 
 void convert_icarus_to_cairnsmore(struct cgpu_info *cm1)

+ 2 - 2
driver-cpu.c

@@ -75,7 +75,7 @@ static inline void affine_to_cpu(__maybe_unused int id, __maybe_unused int cpu)
 
 
 /* TODO: resolve externals */
-extern bool submit_work_sync(struct thr_info *thr, const struct work *work_in);
+extern bool submit_work_sync(struct thr_info *thr, const struct work *work_in, struct timeval *tv);
 extern char *set_int_range(const char *arg, int *i, int min, int max);
 extern int dev_from_id(int thr_id);
 
@@ -834,7 +834,7 @@ CPUSearch:
 	/* if nonce found, submit work */
 	if (unlikely(rc)) {
 		applog(LOG_DEBUG, "CPU %d found something?", dev_from_id(thr_id));
-		if (unlikely(!submit_work_sync(thr, work))) {
+		if (unlikely(!submit_work_sync(thr, work, NULL))) {
 			applog(LOG_ERR, "Failed to submit_work_sync in miner_thread %d", thr_id);
 		}
 		work->blk.nonce = last_nonce + 1;

+ 8 - 4
driver-icarus.c

@@ -55,9 +55,9 @@
 #endif
 
 #include "elist.h"
-#include "fpgautils.h"
 #include "icarus-common.h"
 #include "miner.h"
+#include "fpgautils.h"
 
 // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h
 #define ICARUS_IO_SPEED 115200
@@ -633,7 +633,7 @@ static bool icarus_detect_one(const char *devpath)
 
 static void icarus_detect()
 {
-	serial_detect(icarus_api.dname, icarus_detect_one);
+	serial_detect(&icarus_api, icarus_detect_one);
 }
 
 struct icarus_state {
@@ -711,7 +711,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	struct timeval tv_start, elapsed;
 	struct timeval tv_history_start, tv_history_finish;
 	double Ti, Xi;
-	int i;
+	int curr_hw_errors, i;
+	bool was_hw_error;
 
 	struct ICARUS_HISTORY *history0, *history;
 	int count;
@@ -828,7 +829,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 
 	nonce = be32toh(nonce);
 
+	curr_hw_errors = icarus->hw_errors;
 	submit_nonce(thr, &state->last_work, nonce);
+	was_hw_error = (curr_hw_errors > icarus->hw_errors);
 	memcpy(&state->last_work, work, sizeof(state->last_work));
 
 	hash_count = (nonce & info->nonce_mask);
@@ -870,8 +873,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		}
 	}
 
-	// ignore possible end condition values
+	// ignore possible end condition values ... and hw errors
 	if (info->do_icarus_timing
+	&&  !was_hw_error
 	&&  ((nonce & info->nonce_mask) > END_CONDITION)
 	&&  ((nonce & info->nonce_mask) < (info->nonce_mask & ~END_CONDITION))) {
 		gettimeofday(&tv_history_start, NULL);

+ 2 - 2
driver-modminer.c

@@ -13,9 +13,9 @@
 #include <stdio.h>
 #include <unistd.h>
 
-#include "fpgautils.h"
 #include "logging.h"
 #include "miner.h"
+#include "fpgautils.h"
 
 #define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.ncd"
 #define BISTREAM_USER_ID "\2\4$B"
@@ -138,7 +138,7 @@ modminer_detect_auto()
 static void
 modminer_detect()
 {
-	serial_detect_auto(modminer_api.dname, modminer_detect_one, modminer_detect_auto);
+	serial_detect_auto(&modminer_api, modminer_detect_one, modminer_detect_auto);
 }
 
 #define bailout(...)  return _bailout(-1, modminer, __VA_ARGS__);

+ 1 - 1
driver-ztex.c

@@ -118,7 +118,7 @@ static int ztex_autodetect(void)
 static void ztex_detect()
 {
 	// This wrapper ensures users can specify -S ztex:noauto to disable it
-	noserial_detect(ztex_api.dname, ztex_autodetect);
+	noserial_detect(&ztex_api, ztex_autodetect);
 }
 
 static bool ztex_updateFreq(struct libztex_device* ztex)

+ 26 - 31
fpgautils.c

@@ -51,13 +51,12 @@ enum {
 #endif
 
 #include "elist.h"
-#include "fpgautils.h"
 #include "logging.h"
 #include "miner.h"
+#include "fpgautils.h"
 
 #ifdef HAVE_LIBUDEV
-int
-serial_autodetect_udev(detectone_func_t detectone, const char*prodname)
+int serial_autodetect_udev(detectone_func_t detectone, const char*prodname)
 {
 	struct udev *udev = udev_new();
 	struct udev_enumerate *enumerate = udev_enumerate_new(udev);
@@ -87,15 +86,13 @@ serial_autodetect_udev(detectone_func_t detectone, const char*prodname)
 	return found;
 }
 #else
-int
-serial_autodetect_udev(__maybe_unused detectone_func_t detectone, __maybe_unused const char*prodname)
+int serial_autodetect_udev(__maybe_unused detectone_func_t detectone, __maybe_unused const char*prodname)
 {
 	return 0;
 }
 #endif
 
-int
-serial_autodetect_devserial(detectone_func_t detectone, const char*prodname)
+int serial_autodetect_devserial(detectone_func_t detectone, const char*prodname)
 {
 #ifndef WIN32
 	DIR *D;
@@ -208,32 +205,35 @@ int serial_autodetect_ftdi(__maybe_unused detectone_func_t detectone, __maybe_un
 
 struct device_api *serial_claim(const char *devpath, struct device_api *api);
 
-int
-_serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t autoscan, int flags)
+int _serial_detect(struct device_api *api, detectone_func_t detectone, autoscan_func_t autoscan, int flags)
 {
 	struct string_elist *iter, *tmp;
-	const char*s, *p;
+	const char *dev, *colon;
 	bool inhibitauto = false;
 	char found = 0;
 	bool forceauto = flags & 1;
 	bool hasname;
-	size_t dnamel = strlen(dname);
+	size_t namel = strlen(api->name);
+	size_t dnamel = strlen(api->dname);
 
 	list_for_each_entry_safe(iter, tmp, &scan_devices, list) {
-		s = iter->string;
-		if ((p = strchr(s, ':')) && p[1] != '\0') {
-			size_t plen = p - s;
-			if (plen != dnamel || strncasecmp(s, dname, plen))
+		dev = iter->string;
+		if ((colon = strchr(dev, ':')) && colon[1] != '\0') {
+			size_t idlen = colon - dev;
+
+			// allow either name:device or dname:device
+			if ((idlen != namel || strncasecmp(dev, api->name, idlen))
+			&&  (idlen != dnamel || strncasecmp(dev, api->dname, idlen)))
 				continue;
-			s = p + 1;
+
+			dev = colon + 1;
 			hasname = true;
 		}
 		else
 			hasname = false;
-		if (!strcmp(s, "auto"))
+		if (!strcmp(dev, "auto"))
 			forceauto = true;
-		else
-		if (!strcmp(s, "noauto"))
+		else if (!strcmp(dev, "noauto"))
 			inhibitauto = true;
 		else
 		if ((flags & 2) && !hasname)
@@ -242,13 +242,12 @@ _serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t aut
 		if (!detectone)
 		{}  // do nothing
 		else
-		if (serial_claim(s, NULL))
+		if (serial_claim(dev, NULL))
 		{
-			applog(LOG_DEBUG, "%s is already claimed... skipping probes", s);
+			applog(LOG_DEBUG, "%s is already claimed... skipping probes", dev);
 			string_elist_del(iter);
 		}
-		else
-		if (detectone(s)) {
+		else if (detectone(dev)) {
 			string_elist_del(iter);
 			inhibitauto = true;
 			++found;
@@ -482,8 +481,7 @@ void termios_debug(const char *devpath, struct termios *my_termios, const char *
 /* NOTE: Linux only supports uint8_t (decisecond) timeouts; limiting it in
  *       this interface buys us warnings when bad constants are passed in.
  */
-int
-serial_open(const char*devpath, unsigned long baud, uint8_t timeout, bool purge)
+int serial_open(const char *devpath, unsigned long baud, uint8_t timeout, bool purge)
 {
 #ifdef WIN32
 	HANDLE hSerial = CreateFile(devpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
@@ -600,8 +598,7 @@ serial_open(const char*devpath, unsigned long baud, uint8_t timeout, bool purge)
 #endif
 }
 
-ssize_t
-_serial_read(int fd, char *buf, size_t bufsiz, char *eol)
+ssize_t _serial_read(int fd, char *buf, size_t bufsiz, char *eol)
 {
 	ssize_t len, tlen = 0;
 	while (bufsiz) {
@@ -617,8 +614,7 @@ _serial_read(int fd, char *buf, size_t bufsiz, char *eol)
 	return tlen;
 }
 
-static FILE*
-_open_bitstream(const char*path, const char*subdir, const char*filename)
+static FILE *_open_bitstream(const char *path, const char *subdir, const char *filename)
 {
 	char fullpath[PATH_MAX];
 	strcpy(fullpath, path);
@@ -642,8 +638,7 @@ _open_bitstream(const char*path, const char*subdir, const char*filename)
 	_open_bitstream(path, NULL);  \
 } while(0)
 
-FILE*
-open_bitstream(const char*dname, const char*filename)
+FILE *open_bitstream(const char *dname, const char *filename)
 {
 	FILE *f;
 

+ 17 - 15
fpgautils.h

@@ -15,34 +15,36 @@
 #include <stdio.h>
 #include <unistd.h>
 
+struct device_api;
+
 typedef bool(*detectone_func_t)(const char*);
 typedef int(*autoscan_func_t)();
 
-extern int _serial_detect(const char*dname, detectone_func_t, autoscan_func_t, int flags);
-#define serial_detect_fauto(dname, detectone, autoscan)  \
-	_serial_detect(dname, detectone, autoscan, 1)
-#define serial_detect_auto(dname, detectone, autoscan)  \
-	_serial_detect(dname, detectone, autoscan, 0)
-#define serial_detect_auto_byname(dname, detectone, autoscan)  \
-	_serial_detect(dname, detectone, autoscan, 2)
-#define serial_detect(dname, detectone)  \
-	_serial_detect(dname, detectone,     NULL, 0)
-#define noserial_detect(dname, autoscan)  \
-	_serial_detect(dname, NULL     , autoscan, 0)
+extern int _serial_detect(struct device_api *api, detectone_func_t, autoscan_func_t, int flags);
+#define serial_detect_fauto(api, detectone, autoscan)  \
+	_serial_detect(api, detectone, autoscan, 1)
+#define serial_detect_auto(api, detectone, autoscan)  \
+	_serial_detect(api, detectone, autoscan, 0)
+#define serial_detect_auto_byname(api, detectone, autoscan)  \
+	_serial_detect(api, detectone, autoscan, 2)
+#define serial_detect(api, detectone)  \
+	_serial_detect(api, detectone,     NULL, 0)
+#define noserial_detect(api, autoscan)  \
+	_serial_detect(api, NULL     , autoscan, 0)
 extern int serial_autodetect_devserial(detectone_func_t, const char*prodname);
 extern int serial_autodetect_udev     (detectone_func_t, const char*prodname);
 extern int serial_autodetect_ftdi     (detectone_func_t, const char*needle, const char *needle2);
 
 extern struct device_api *serial_claim(const char *devpath, struct device_api *);
 
-extern int serial_open(const char*devpath, unsigned long baud, uint8_t timeout, bool purge);
-extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char*eol);
+extern int serial_open(const char *devpath, unsigned long baud, uint8_t timeout, bool purge);
+extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol);
 #define serial_read(fd, buf, count)  \
 	_serial_read(fd, (char*)(buf), count, NULL)
 #define serial_read_line(fd, buf, bufsiz, eol)  \
-	_serial_read(fd, buf, count, &eol)
+	_serial_read(fd, buf, bufsiz, &eol)
 #define serial_close(fd)  close(fd)
 
-extern FILE*open_bitstream(const char*dname, const char*filename);
+extern FILE *open_bitstream(const char *dname, const char *filename);
 
 #endif

+ 1 - 1
libztex.c

@@ -23,8 +23,8 @@
 #include <stdio.h>
 #include <unistd.h>
 
-#include "fpgautils.h"
 #include "miner.h"
+#include "fpgautils.h"
 #include "libztex.h"
 
 #define BUFSIZE 256

+ 174 - 50
miner.c

@@ -159,6 +159,7 @@ bool opt_delaynet;
 bool opt_disable_pool = true;
 char *opt_icarus_options = NULL;
 char *opt_icarus_timing = NULL;
+bool opt_worktime;
 
 char *opt_kernel_path;
 char *cgminer_path;
@@ -209,6 +210,7 @@ int hw_errors;
 int total_accepted, total_rejected, total_diff1;
 float total_accepted_weighed;
 int total_getworks, total_stale, total_discarded;
+double total_diff_accepted, total_diff_rejected, total_diff_stale;
 static int total_queued, staged_rollable;
 unsigned int new_blocks;
 unsigned int found_blocks;
@@ -368,7 +370,7 @@ static void sharelog(const char*disposition, const struct work*work)
 	thr_id = work->thr_id;
 	cgpu = thr_info[thr_id].cgpu;
 	pool = work->pool;
-	t = (unsigned long int)work->share_found_time;
+	t = (unsigned long int)(work->tv_work_found.tv_sec);
 	target = bin2hex(work->target, sizeof(work->target));
 	if (unlikely(!target)) {
 		applog(LOG_ERR, "sharelog target OOM");
@@ -1195,6 +1197,9 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITH_ARG("--userpass|-O",
 		     set_userpass, NULL, NULL,
 		     "Username:Password pair for bitcoin JSON-RPC server"),
+	OPT_WITHOUT_ARG("--worktime",
+			opt_set_bool, &opt_worktime,
+			"Display extra work time debug information"),
 	OPT_WITH_ARG("--pools",
 			opt_set_bool, NULL, NULL, opt_hidden),
 	OPT_ENDTABLE
@@ -1449,8 +1454,6 @@ static void calc_midstate(struct work *work)
 
 static bool work_decode(const json_t *val, struct work *work)
 {
-	unsigned char bits = 0, i;
-	
 	if (unlikely(detect_algo == 1)) {
 		json_t *tmp = json_object_get(val, "algorithm");
 		const char *v = tmp ? json_string_value(tmp) : "";
@@ -1522,22 +1525,6 @@ static bool work_decode(const json_t *val, struct work *work)
 			work->target[p] = c;
 		}
 	}
-	
-	for (i = 32; i--; )
-	{
-		if (work->target[i])
-		{
-			unsigned char j = ~work->target[i];
-			while (j & 0x80)
-			{
-				++bits;
-				j <<= 1;
-			}
-			break;
-		}
-		bits += 8;
-	}
-	work->difficulty = pow(2, bits - 32);
 
 	memset(work->hash, 0, sizeof(work->hash));
 
@@ -2097,7 +2084,9 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub
 	struct pool *pool = work->pool;
 	int rolltime;
 	uint32_t *hash32;
+	struct timeval tv_submit, tv_submit_reply;
 	char hashshow[64+1] = "";
+	char worktime[200] = "";
 
 	if (work->tmpl) {
 		unsigned char data[76];
@@ -2128,12 +2117,14 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub
 
 	applog(LOG_DEBUG, "DBG: sending %s submit RPC call: %s", pool->rpc_url, sd);
 
+	gettimeofday(&tv_submit, NULL);
 	/* issue JSON-RPC request */
 	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, false, false, &rolltime, pool, true);
 
 	free(s);
 	free(sd);
 
+	gettimeofday(&tv_submit_reply, NULL);
 	if (unlikely(!val)) {
 		applog(LOG_INFO, "submit_upstream_work json_rpc_call failed");
 		if (!pool_tset(pool, &pool->submit_fail)) {
@@ -2152,9 +2143,52 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub
 		if (opt_scrypt)
 			sprintf(hashshow, "%08lx.%08lx", (unsigned long)(hash32[7]), (unsigned long)(hash32[6]));
 		else {
-			sprintf(hashshow, "%08lx.%08lx%s", (unsigned long)(hash32[6]), (unsigned long)(hash32[5]),
+			int intdiff = round(work->work_difficulty);
+
+			sprintf(hashshow, "%08lx Diff %d%s", (unsigned long)(hash32[6]), intdiff,
 				work->block? " BLOCK!" : "");
 		}
+
+		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;
+
+			tm = localtime(&(work->tv_getwork.tv_sec));
+			memcpy(&tm_getwork, tm, sizeof(struct tm));
+			tm = localtime(&(tv_submit_reply.tv_sec));
+			memcpy(&tm_submit_reply, tm, sizeof(struct tm));
+
+			if (work->clone) {
+				sprintf(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;
+
+			sprintf(worktime, " <-%08lx.%08lx M:%c D:%1.*f G:%02d:%02d:%02d:%1.3f %s (%1.3f) W:%1.3f (%1.3f) S:%1.3f R:%02d:%02d:%02d",
+				(unsigned long)swab32(*(uint32_t *)&(work->data[opt_scrypt ? 32 : 28])),
+				(unsigned long)swab32(*(uint32_t *)&(work->data[opt_scrypt ? 28 : 24])),
+				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);
+		}
 	}
 
 	/* Theoretically threads could race when modifying accepted and
@@ -2162,22 +2196,27 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub
 	 * same time is zero so there is no point adding extra locking */
 	if (json_is_null(res) || json_is_true(res)) {
 		cgpu->accepted++;
-		cgpu->accepted_weighed += work->difficulty;
+		cgpu->accepted_weighed += work->work_difficulty;
 		total_accepted++;
-		total_accepted_weighed += work->difficulty;
+		total_accepted_weighed += work->work_difficulty;
 		pool->accepted++;
+		cgpu->diff_accepted += work->work_difficulty;
+		total_diff_accepted += work->work_difficulty;
+		pool->diff_accepted += work->work_difficulty;
 		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",
-				       hashshow, cgpu->api->name, cgpu->device_id, work->pool->pool_no, resubmit ? "(resubmit)" : "");
+				applog(LOG_NOTICE, "Accepted %s %s %d pool %d %s%s",
+				       hashshow, cgpu->api->name, cgpu->device_id, work->pool->pool_no, resubmit ? "(resubmit)" : "", worktime);
 			else
-				applog(LOG_NOTICE, "Accepted %s %s %d %s",
-				       hashshow, cgpu->api->name, cgpu->device_id, resubmit ? "(resubmit)" : "");
+				applog(LOG_NOTICE, "Accepted %s %s %d %s%s",
+				       hashshow, cgpu->api->name, cgpu->device_id, resubmit ? "(resubmit)" : "", worktime);
 		}
 		sharelog("accept", work);
 		if (opt_shares && total_accepted >= opt_shares) {
@@ -2199,6 +2238,9 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub
 		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++;
 		applog(LOG_DEBUG, "PROOF OF WORK RESULT: false (booooo)");
 		if (!QUIET) {
@@ -2227,8 +2269,8 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub
 			} else
 				strcpy(reason, "");
 
-			applog(LOG_NOTICE, "Rejected %s %s %d %s%s %s",
-			       hashshow, cgpu->api->name, cgpu->device_id, where, reason, resubmit ? "(resubmit)" : "");
+			applog(LOG_NOTICE, "Rejected %s %s %d %s%s %s%s",
+			       hashshow, cgpu->api->name, cgpu->device_id, where, reason, resubmit ? "(resubmit)" : "", worktime);
 			sharelog(disposition, work);
 		}
 
@@ -2329,6 +2371,42 @@ static inline struct pool *select_pool(bool lagging)
 	return pool;
 }
 
+static double DIFFEXACTONE = 26959946667150639794667015087019630673637144422540572481103610249216.0;
+
+/*
+ * Calculate the work share difficulty
+ */
+static void calc_diff(struct work *work)
+{
+	struct cgminer_pool_stats *pool_stats = &(work->pool->cgminer_pool_stats);
+	double targ;
+	int i;
+
+	targ = 0;
+	for (i = 31; i >= 0; i--) {
+		targ *= 256;
+		targ += work->target[i];
+	}
+
+	work->work_difficulty = DIFFEXACTONE / (targ ? : DIFFEXACTONE);
+
+	pool_stats->last_diff = work->work_difficulty;
+
+	if (work->work_difficulty == pool_stats->min_diff)
+		pool_stats->min_diff_count++;
+	else if (work->work_difficulty < pool_stats->min_diff || pool_stats->min_diff == 0) {
+		pool_stats->min_diff = work->work_difficulty;
+		pool_stats->min_diff_count = 1;
+	}
+
+	if (work->work_difficulty == pool_stats->max_diff)
+		pool_stats->max_diff_count++;
+	else if (work->work_difficulty > pool_stats->max_diff) {
+		pool_stats->max_diff = work->work_difficulty;
+		pool_stats->max_diff_count = 1;
+	}
+}
+
 static void get_benchmark_work(struct work *work)
 {
 	// Use a random work block pulled from a pool
@@ -2341,6 +2419,10 @@ static void get_benchmark_work(struct work *work)
 	memcpy(work, &bench_block, min_size);
 	work->mandatory = true;
 	work->pool = pools[0];
+	gettimeofday(&(work->tv_getwork), NULL);
+	memcpy(&(work->tv_getwork_reply), &(work->tv_getwork), sizeof(struct timeval));
+	work->getwork_mode = GETWORK_MODE_BENCHMARK;
+	calc_diff(work);
 }
 
 static char *prepare_rpc_req(struct work *work, enum pool_protocol proto, const char *lpid)
@@ -2411,7 +2493,7 @@ static bool get_upstream_work(struct work *work, CURL *curl)
 {
 	struct pool *pool = work->pool;
 	struct cgminer_pool_stats *pool_stats = &(pool->cgminer_pool_stats);
-	struct timeval tv_start, tv_end, tv_elapsed;
+	struct timeval tv_elapsed;
 	json_t *val = NULL;
 	bool rc = false;
 	char *url;
@@ -2428,7 +2510,7 @@ tryagain:
 
 	url = pool->rpc_url;
 
-	gettimeofday(&tv_start, NULL);
+	gettimeofday(&(work->tv_getwork), NULL);
 
 	val = json_rpc_call(curl, url, pool->rpc_userpass, rpc_req, false,
 			    false, &work->rolltime, pool, false);
@@ -2447,8 +2529,8 @@ tryagain:
 	} else
 		applog(LOG_DEBUG, "Failed json_rpc_call in get_upstream_work");
 
-	gettimeofday(&tv_end, NULL);
-	timersub(&tv_end, &tv_start, &tv_elapsed);
+	gettimeofday(&(work->tv_getwork_reply), NULL);
+	timersub(&(work->tv_getwork_reply), &(work->tv_getwork), &tv_elapsed);
 	pool_stats->getwork_wait_rolling += ((double)tv_elapsed.tv_sec + ((double)tv_elapsed.tv_usec / 1000000)) * 0.63;
 	pool_stats->getwork_wait_rolling /= 1.63;
 
@@ -2465,6 +2547,8 @@ tryagain:
 
 	work->pool = pool;
 	work->longpoll = false;
+	work->getwork_mode = GETWORK_MODE_POOL;
+	calc_diff(work);
 	total_getworks++;
 	pool->getwork_requested++;
 
@@ -2818,6 +2902,7 @@ static struct work *make_clone(struct work *work)
 
 	workcpy(work_clone, work);
 	work_clone->clone = true;
+	gettimeofday((struct timeval *)&(work_clone->tv_cloned), NULL);
 	work_clone->longpoll = false;
 	work_clone->mandatory = false;
 	/* Make cloned work appear slightly older to bias towards keeping the
@@ -2862,6 +2947,15 @@ out:
 
 static bool queue_request(void);
 
+static void pool_died(struct pool *pool)
+{
+	if (!pool_tset(pool, &pool->idle)) {
+		applog(LOG_WARNING, "Pool %d %s not responding!", pool->pool_no, pool->rpc_url);
+		gettimeofday(&pool->tv_idle, NULL);
+		switch_pools(NULL);
+	}
+}
+
 static void *get_work_thread(void *userdata)
 {
 	struct workio_cmd *wc = (struct workio_cmd *)userdata;
@@ -2896,13 +2990,17 @@ static void *get_work_thread(void *userdata)
 
 		/* obtain new work from bitcoin via JSON-RPC */
 		if (!get_upstream_work(ret_work, ce->curl)) {
-			/* pause, then restart work-request loop */
 			applog(LOG_DEBUG, "json_rpc_call failed on get work, retrying");
 			dec_queued(pool);
+			/* Make sure the pool just hasn't stopped serving
+			 * requests but is up as we'll keep hammering it */
+			if (++pool->seq_getfails > mining_threads + opt_queue)
+				pool_died(pool);
 			queue_request();
 			free_work(ret_work);
 			goto out;
 		}
+		pool->seq_getfails = 0;
 
 		ret_work->queued = true;
 	}
@@ -3030,6 +3128,8 @@ static void submit_discard_share(struct work *work)
 	sharelog("discard", work);
 	++total_stale;
 	++(work->pool->stale_shares);
+	total_diff_stale += work->work_difficulty;
+	work->pool->diff_stale += work->work_difficulty;
 }
 
 static void *submit_work_thread(void *userdata)
@@ -3658,6 +3758,8 @@ static void display_pool_summary(struct pool *pool)
 		wlog(" Share submissions: %d\n", pool->accepted + pool->rejected);
 		wlog(" Accepted shares: %d\n", pool->accepted);
 		wlog(" Rejected shares: %d\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));
 		efficiency = pool->getwork_requested ? pool->accepted * 100.0 / pool->getwork_requested : 0.0;
@@ -4093,12 +4195,13 @@ static void display_options(void)
 	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[L]og interval:%d\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\n[L]og interval:%d\n",
 		opt_debug_console ? "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_log_interval);
 	wlogprint("Select an option or any other key to return\n");
 	input = getch();
@@ -4152,6 +4255,10 @@ retry:
 		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
 		clear_logwin();
 
@@ -4557,6 +4664,7 @@ static char *absolute_uri(char *uri, const char *ref)
 
 static bool pool_active(struct pool *pool, bool pinging)
 {
+	struct timeval tv_getwork, tv_getwork_reply;
 	bool ret = false;
 	json_t *val;
 	CURL *curl;
@@ -4581,8 +4689,10 @@ tryagain:
 		return false;
 
 	applog(LOG_INFO, "Testing pool %s", pool->rpc_url);
+	gettimeofday(&tv_getwork, NULL);
 	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, rpc_req,
 			true, false, &rolltime, pool, false);
+	gettimeofday(&tv_getwork_reply, NULL);
 
 	if (val) {
 		bool rc;
@@ -4598,6 +4708,10 @@ tryagain:
 			       pool->pool_no, pool->rpc_url);
 			work->pool = pool;
 			work->rolltime = rolltime;
+			memcpy(&(work->tv_getwork), &tv_getwork, sizeof(struct timeval));
+			memcpy(&(work->tv_getwork_reply), &tv_getwork_reply, sizeof(struct timeval));
+			work->getwork_mode = GETWORK_MODE_TESTPOOL;
+			calc_diff(work);
 			applog(LOG_DEBUG, "Pushing pooltest work to base pool");
 
 			tq_push(thr_info[stage_thr_id].q, work);
@@ -4662,15 +4776,6 @@ out:
 	return ret;
 }
 
-static void pool_died(struct pool *pool)
-{
-	if (!pool_tset(pool, &pool->idle)) {
-		applog(LOG_WARNING, "Pool %d %s not responding!", pool->pool_no, pool->rpc_url);
-		gettimeofday(&pool->tv_idle, NULL);
-		switch_pools(NULL);
-	}
-}
-
 static inline int cp_prio(void)
 {
 	int prio;
@@ -4897,7 +5002,7 @@ out:
 	work->mined = true;
 }
 
-bool submit_work_sync(struct thr_info *thr, const struct work *work_in)
+bool submit_work_sync(struct thr_info *thr, const struct work *work_in, struct timeval *tv_work_found)
 {
 	struct workio_cmd *wc;
 
@@ -4912,7 +5017,8 @@ bool submit_work_sync(struct thr_info *thr, const struct work *work_in)
 	wc->cmd = WC_SUBMIT_WORK;
 	wc->thr = thr;
 	workcpy(wc->work, work_in);
-	wc->work->share_found_time = time(NULL);
+	if (tv_work_found)
+		memcpy(&(wc->work->tv_work_found), tv_work_found, sizeof(struct timeval));
 
 	applog(LOG_DEBUG, "Pushing submit work to work thread");
 
@@ -4972,6 +5078,9 @@ bool test_nonce(struct work *work, uint32_t nonce, bool checktarget)
 
 bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce)
 {
+	struct timeval tv_work_found;
+	gettimeofday(&tv_work_found, NULL);
+
 	total_diff1++;
 	thr->cgpu->diff1++;
 	work->pool->diff1++;
@@ -4982,7 +5091,7 @@ bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce)
 		applog(LOG_INFO, "Pool %d share below target", work->pool->pool_no);
 		return true;
 	}
-	return submit_work_sync(thr, work);
+	return submit_work_sync(thr, work, &tv_work_found);
 }
 
 static inline bool abandon_work(struct work *work, struct timeval *wdiff, uint64_t hashes)
@@ -5002,7 +5111,9 @@ static void mt_disable(struct thr_info *mythr, const int thr_id,
 	mythr->rolling = mythr->cgpu->rolling = 0;
 	applog(LOG_DEBUG, "Popping wakeup ping in miner thread");
 	thread_reportout(mythr);
-	tq_pop(mythr->q, NULL); /* Ignore ping that's popped */
+	do {
+		tq_pop(mythr->q, NULL); /* Ignore ping that's popped */
+	} while (mythr->pause);
 	thread_reportin(mythr);
 	applog(LOG_WARNING, "Thread %d being re-enabled", thr_id);
 	if (api->thread_enable)
@@ -5103,6 +5214,8 @@ void *miner_thread(void *userdata)
 			}
 			pool_stats->getwork_calls++;
 
+			gettimeofday(&(work->tv_work_start), NULL);
+
 			thread_reportin(mythr);
 			hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce);
 			thread_reportin(mythr);
@@ -5199,7 +5312,7 @@ enum {
 };
 
 /* 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 work *work)
+static void convert_to_work(json_t *val, int rolltime, struct pool *pool, struct work *work, struct timeval *tv_lp, struct timeval *tv_lp_reply)
 {
 	bool rc;
 
@@ -5212,6 +5325,10 @@ static void convert_to_work(json_t *val, int rolltime, struct pool *pool, struct
 	work->pool = pool;
 	work->rolltime = rolltime;
 	work->longpoll = true;
+	memcpy(&(work->tv_getwork), tv_lp, sizeof(struct timeval));
+	memcpy(&(work->tv_getwork_reply), tv_lp_reply, sizeof(struct timeval));
+	work->getwork_mode = GETWORK_MODE_LP;
+	calc_diff(work);
 
 	if (pool->enabled == POOL_REJECTING)
 		work->mandatory = true;
@@ -5287,7 +5404,7 @@ static void *longpoll_thread(void *userdata)
 	/* This *pool is the source of the actual longpoll, not the pool we've
 	 * tied it to */
 	struct pool *pool = NULL;
-	struct timeval start, end;
+	struct timeval start, reply, end;
 	CURL *curl = NULL;
 	int failures = 0;
 	int rolltime;
@@ -5344,13 +5461,16 @@ retry_pool:
 		val = json_rpc_call(curl, pool->lp_url, pool->rpc_userpass, rpc_req,
 				    false, true, &rolltime, pool, false);
 		pool->lp_socket = CURL_SOCKET_BAD;
+
+		gettimeofday(&reply, NULL);
+
 		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, work);
+			convert_to_work(val, rolltime, pool, work, &start, &reply);
 			failures = 0;
 			json_decref(val);
 		} else {
@@ -5726,6 +5846,8 @@ static void print_summary(void)
 	applog(LOG_WARNING, "Share submissions: %d", total_accepted + total_rejected);
 	applog(LOG_WARNING, "Accepted shares: %d", total_accepted);
 	applog(LOG_WARNING, "Rejected shares: %d", 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);
@@ -5750,6 +5872,8 @@ static void print_summary(void)
 			applog(LOG_WARNING, " Share submissions: %d", pool->accepted + pool->rejected);
 			applog(LOG_WARNING, " Accepted shares: %d", pool->accepted);
 			applog(LOG_WARNING, " Rejected shares: %d", 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));
 			efficiency = pool->getwork_requested ? pool->accepted * 100.0 / pool->getwork_requested : 0.0;

+ 37 - 3
miner.h

@@ -278,6 +278,7 @@ struct device_api {
 	struct api_data* (*get_api_extra_device_status)(struct cgpu_info*);
 	struct api_data *(*get_api_stats)(struct cgpu_info*);
 	bool (*get_stats)(struct cgpu_info*);
+	void (*identify_device)(struct cgpu_info*); // e.g. to flash a led
 
 	// Thread-specific functions
 	bool (*thread_prepare)(struct thr_info*);
@@ -315,6 +316,7 @@ enum dev_reason {
 	REASON_DEV_OVER_HEAT,
 	REASON_DEV_THERMAL_CUTOFF,
 	REASON_DEV_COMMS_ERROR,
+	REASON_DEV_THROTTLE,
 };
 
 #define REASON_NONE			"None"
@@ -327,6 +329,7 @@ enum dev_reason {
 #define REASON_DEV_OVER_HEAT_STR	"Device over heated"
 #define REASON_DEV_THERMAL_CUTOFF_STR	"Device reached thermal cutoff"
 #define REASON_DEV_COMMS_ERROR_STR	"Device comms error"
+#define REASON_DEV_THROTTLE_STR		"Device throttle"
 #define REASON_UNKNOWN_STR		"Unknown reason - code bug"
 
 #define MIN_SEC_UNSET 99999999
@@ -350,6 +353,11 @@ struct cgminer_pool_stats {
 	bool canroll;
 	bool hadexpire;
 	uint32_t rolltime;
+	double min_diff;
+	double max_diff;
+	double last_diff;
+	uint32_t min_diff_count;
+	uint32_t max_diff_count;
 };
 
 struct cgpu_info {
@@ -375,6 +383,7 @@ struct cgpu_info {
 	uint32_t nonces;
 	bool nonce_range;
 	bool polling;
+	bool flash_led;
 #endif
 	void *cgpu_data;
 	pthread_mutex_t		device_mutex;
@@ -439,8 +448,11 @@ struct cgpu_info {
 	float gpu_vddc;
 #endif
 	int diff1;
+	double diff_accepted;
+	double diff_rejected;
 	int last_share_pool;
 	time_t last_share_pool_time;
+	double last_share_diff;
 
 	time_t device_last_well;
 	time_t device_last_not_well;
@@ -454,6 +466,7 @@ struct cgpu_info {
 	int dev_over_heat_count;	// It's a warning but worth knowing
 	int dev_thermal_cutoff_count;
 	int dev_comms_error_count;
+	int dev_throttle_count;
 
 	struct cgminer_stats cgminer_stats;
 };
@@ -496,6 +509,7 @@ extern void thr_info_cancel(struct thr_info *thr);
 extern void thr_info_freeze(struct thr_info *thr);
 extern void nmsleep(unsigned int msecs);
 extern void rename_thr(const char* name);
+extern double tdiff(struct timeval *end, struct timeval *start);
 
 struct string_elist {
 	char *string;
@@ -637,6 +651,7 @@ extern bool opt_delaynet;
 extern bool opt_restart;
 extern char *opt_icarus_options;
 extern char *opt_icarus_timing;
+extern bool opt_worktime;
 #ifdef USE_BITFORCE
 extern bool opt_bfl_noncerange;
 #endif
@@ -738,6 +753,7 @@ extern unsigned int new_blocks;
 extern unsigned int found_blocks;
 extern int total_accepted, total_rejected, total_diff1;;
 extern int total_getworks, total_stale, total_discarded;
+extern double total_diff_accepted, total_diff_rejected, total_diff_stale;
 extern unsigned int local_work;
 extern unsigned int total_go, total_ro;
 extern const int opt_cutofftemp;
@@ -806,9 +822,14 @@ struct pool {
 	int prio;
 	int accepted, rejected;
 	int seq_rejects;
+	int seq_getfails;
 	int solved;
 	int diff1;
 
+	double diff_accepted;
+	double diff_rejected;
+	double diff_stale;
+
 	int queued;
 	int staged;
 
@@ -860,11 +881,17 @@ struct pool {
 	struct list_head curlring;
 
 	time_t last_share_time;
+	double last_share_diff;
 
 	struct cgminer_stats cgminer_stats;
 	struct cgminer_pool_stats cgminer_pool_stats;
 };
 
+#define GETWORK_MODE_TESTPOOL 'T'
+#define GETWORK_MODE_POOL 'P'
+#define GETWORK_MODE_LP 'L'
+#define GETWORK_MODE_BENCHMARK 'B'
+
 struct work {
 	unsigned char	data[128];
 	unsigned char	hash1[64];
@@ -897,13 +924,18 @@ struct work {
 	int		id;
 	UT_hash_handle hh;
 	
-	float		difficulty;
-
-	time_t share_found_time;
+	double		work_difficulty;
 
 	blktemplate_t	*tmpl;
 	int		*tmpl_refcount;
 	unsigned int	dataid;
+
+	struct timeval	tv_getwork;
+	struct timeval	tv_getwork_reply;
+	struct timeval	tv_cloned;
+	struct timeval	tv_work_start;
+	struct timeval	tv_work_found;
+	char		getwork_mode;
 };
 
 extern void get_datestamp(char *, struct timeval *);
@@ -950,6 +982,7 @@ enum api_data_type {
 	API_FREQ,
 	API_VOLTS,
 	API_HS,
+	API_DIFF,
 	API_JSON,
 };
 
@@ -981,6 +1014,7 @@ extern struct api_data *api_add_utility(struct api_data *root, char *name, doubl
 extern struct api_data *api_add_freq(struct api_data *root, char *name, double *data, bool copy_data);
 extern struct api_data *api_add_volts(struct api_data *root, char *name, float *data, bool copy_data);
 extern struct api_data *api_add_hs(struct api_data *root, char *name, double *data, bool copy_data);
+extern struct api_data *api_add_diff(struct api_data *root, char *name, double *data, bool copy_data);
 extern struct api_data *api_add_json(struct api_data *root, char *name, json_t *data, bool copy_data);
 
 #endif /* __MINER_H__ */

+ 34 - 7
miner.php

@@ -2,7 +2,7 @@
 session_start();
 #
 global $title, $miner, $port, $readonly, $notify, $rigs;
-global $rgisecurity, $rigtotals, $forcerigtotals;
+global $rigipsecurity, $rigtotals, $forcerigtotals;
 global $socksndtimeoutsec, $sockrcvtimeoutsec;
 global $checklastshare, $poolinputs, $hidefields;
 global $ignorerefresh, $changerefresh, $autorefresh;
@@ -523,7 +523,15 @@ function classlastshare($when, $alldata, $warnclass, $errorclass)
  if (!isset($alldata['Last Share Time']))
 	return '';
 
+ if (!isset($alldata['Last Share Difficulty']))
+	return '';
+
  $expected = pow(2, 32) / ($alldata['MHS av'] * pow(10, 6));
+
+ // If the share difficulty changes while waiting on a share,
+ // this calculation will of course be incorrect
+ $expected *= $alldata['Last Share Difficulty'];
+
  $howlong = $when - $alldata['Last Share Time'];
  if ($howlong < 1)
 	$howlong = 1;
@@ -658,11 +666,20 @@ function fmt($section, $name, $value, $when, $alldata)
 		if ($value == 0)
 			$class = $errorclass;
 		else
-			if (isset($alldata['MHS av']))
+			if (isset($alldata['Difficulty Accepted'])
+			&&  isset($alldata['Accepted'])
+			&&  isset($alldata['MHS av'])
+			&&  ($alldata['Difficulty Accepted'] > 0)
+			&&  ($alldata['Accepted'] > 0))
 			{
 				$expected = 60 * $alldata['MHS av'] * (pow(10, 6) / pow(2, 32));
 				if ($expected == 0)
 					$expected = 0.000001; // 1 H/s
+
+				$da = $alldata['Difficulty Accepted'];
+				$a = $alldata['Accepted'];
+				$expected /= ($da / $a);
+
 				$ratio = $value / $expected;
 				if ($ratio < 0.9)
 					$class = $loclass;
@@ -726,16 +743,26 @@ function fmt($section, $name, $value, $when, $alldata)
 			$dec = '';
 		else
 			$dec = '.'.$parts[1];
-		$ret = number_format($parts[0]).$dec;
+		$ret = number_format((float)$parts[0]).$dec;
 
 		if ($value == 0)
 			$class = $errorclass;
 		else
-			if (isset($alldata['Utility']))
+			if (isset($alldata['Difficulty Accepted'])
+			&&  isset($alldata['Accepted'])
+			&&  isset($alldata['Utility'])
+			&&  ($alldata['Difficulty Accepted'] > 0)
+			&&  ($alldata['Accepted'] > 0))
 			{
 				$expected = 60 * $value * (pow(10, 6) / pow(2, 32));
-				$utility = $alldata['Utility'];
-				$ratio = $utility / $expected;
+				if ($expected == 0)
+					$expected = 0.000001; // 1 H/s
+
+				$da = $alldata['Difficulty Accepted'];
+				$a = $alldata['Accepted'];
+				$expected /= ($da / $a);
+
+				$ratio = $alldata['Utility'] / $expected;
 				if ($ratio < 0.9)
 					$class = $hiclass;
 				else
@@ -773,7 +800,7 @@ function fmt($section, $name, $value, $when, $alldata)
 			$dec = '';
 		else
 			$dec = '.'.$parts[1];
-		$ret = number_format($parts[0]).$dec;
+		$ret = number_format((float)$parts[0]).$dec;
 		break;
 	case 'GPU.Status':
 	case 'PGA.Status':

+ 6 - 0
util.c

@@ -766,3 +766,9 @@ void rename_thr(const char* name) {
 	(void)name;
 #endif
 }
+
+/* Returns the seconds difference between end and start times as a double */
+double tdiff(struct timeval *end, struct timeval *start)
+{
+	return end->tv_sec - start->tv_sec + (end->tv_usec - start->tv_usec) / 1000000.0;
+}