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
                               stating the results of the disable request
                               This is only available if PGA mining is enabled
                               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
  devdetails    DEVDETAILS     Each device with a list of their static details
                               This lists all devices including those not supported
                               This lists all devices including those not supported
                               by the 'devs' command
                               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
                               the screen curses debug settings
                               You can only specify one setting
                               You can only specify one setting
                               Only the first character is checked (case insensitive):
                               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):
                               The output fields are (as above):
                               Silent=true/false,
                               Silent=true/false,
                               Quiet=true/false,
                               Quiet=true/false,
                               Verbose=true/false,
                               Verbose=true/false,
                               Debug=true/false,
                               Debug=true/false,
                               RPCProto=true/false,
                               RPCProto=true/false,
-                              PerDevice=true/false|
+                              PerDevice=true/false,
+                              WorkTime=true/false|
 
 
  setconfig|name,N (*)
  setconfig|name,N (*)
                none           There is no reply section just the STATUS section
                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:
 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)
 API V1.19 (BFGMiner v2.8.0)
 
 
 Added API commands:
 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
 BFGMiner Version 2.8.0 - September 15, 2012
 
 
 - Be specific about jansson version requirement
 - 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,
 To force the code to only attempt detection with a specific driver,
 prepend the argument with the driver name followed by a colon.
 prepend the argument with the driver name followed by a colon.
 For example, "icarus:/dev/ttyUSB0" or "bitforce:\\.\COM5"
 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
 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
 There is also the -m option on linux which will spawn a command of your choice
 and pipe the output directly to that command.
 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
 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
 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
  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
 This means your account must have the group 'dialout' or root priviledges
 To permanently give your account the 'dialout' group:
 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
 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_FOO 77
 #define MSG_MINECOIN 78
 #define MSG_MINECOIN 78
 #define MSG_DEBUGSET 79
 #define MSG_DEBUGSET 79
+#define MSG_PGAIDENT 80
+#define MSG_PGANOID 81
 
 
 #define MSG_SETCONFIG 82
 #define MSG_SETCONFIG 82
 #define MSG_UNKCON 83
 #define MSG_UNKCON 83
@@ -557,7 +559,10 @@ struct CODES {
  { SEVERITY_SUCC,  MSG_FOO,	PARAM_BOOL,	"Failover-Only set to %s" },
  { SEVERITY_SUCC,  MSG_FOO,	PARAM_BOOL,	"Failover-Only set to %s" },
  { SEVERITY_SUCC,  MSG_MINECOIN,PARAM_NONE,	"BFGMiner coin" },
  { SEVERITY_SUCC,  MSG_MINECOIN,PARAM_NONE,	"BFGMiner coin" },
  { SEVERITY_SUCC,  MSG_DEBUGSET,PARAM_STR,	"Debug settings" },
  { 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_SUCC,  MSG_SETCONFIG,PARAM_SET,	"Set config '%s' to %d" },
  { SEVERITY_ERR,   MSG_UNKCON,	PARAM_STR,	"Unknown config '%s'" },
  { SEVERITY_ERR,   MSG_UNKCON,	PARAM_STR,	"Unknown config '%s'" },
  { SEVERITY_ERR,   MSG_INVNUM,	PARAM_BOTH,	"Invalid number (%d) for '%s' range is 0-9999" },
  { 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 int my_thr_id = 0;
 static bool bye;
 static bool bye;
+#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
 static bool ping = true;
 static bool ping = true;
+#endif
 
 
 // Used to control quit restart access to shutdown variables
 // Used to control quit restart access to shutdown variables
 static pthread_mutex_t quit_restart_lock;
 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_UTILITY:
 			case API_FREQ:
 			case API_FREQ:
 			case API_HS:
 			case API_HS:
+			case API_DIFF:
 				api_data->data = (void *)malloc(sizeof(double));
 				api_data->data = (void *)malloc(sizeof(double));
 				*((double *)(api_data->data)) = *((double *)data);
 				*((double *)(api_data->data)) = *((double *)data);
 				break;
 				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);
 	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)
 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);
 	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:
 			case API_HS:
 				sprintf(buf, "%.15f", *((double *)(root->data)));
 				sprintf(buf, "%.15f", *((double *)(root->data)));
 				break;
 				break;
+			case API_DIFF:
+				sprintf(buf, "%.8f", *((double *)(root->data)));
+				break;
 			case API_BOOL:
 			case API_BOOL:
 				sprintf(buf, "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR);
 				sprintf(buf, "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR);
 				break;
 				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_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_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false);
 	root = api_add_int(root, "Diff1 Work", &(cgpu->diff1), 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)
 	if (cgpu->api->get_api_extra_device_status)
 		root = api_add_extra(root, cgpu->api->get_api_extra_device_status(cgpu));
 		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));
 	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
 #endif
 
 
 #ifdef WANT_CPUMINE
 #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_uint(root, "Remote Failures", &(pool->remotefail_occasions), false);
 		root = api_add_escape(root, "User", pool->rpc_user, 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_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) {
 		if (pool->rpc_proxy) {
 			root = api_add_escape(root, "Proxy", pool->rpc_proxy, false);
 			root = api_add_escape(root, "Proxy", pool->rpc_proxy, false);
 		} else {
 		} else {
 			root = api_add_const(root, "Proxy", BLANK, false);
 			root = api_add_const(root, "Proxy", BLANK, false);
 		}
 		}
+		root = api_add_diff(root, "Difficulty Accepted", &(pool->diff_accepted), false);
+		root = api_add_diff(root, "Difficulty Rejected", &(pool->diff_rejected), false);
+		root = api_add_diff(root, "Difficulty Stale", &(pool->diff_stale), false);
+		root = api_add_diff(root, "Last Share Difficulty", &(pool->last_share_diff), false);
 
 
 		if (isjson && (i > 0))
 		if (isjson && (i > 0))
 			strcat(io_buffer, COMMA);
 			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_uint(root, "Network Blocks", &(new_blocks), false);
 	root = api_add_mhtotal(root, "Total MH", &(total_mhashes_done), 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_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);
 	root = print_data(root, buf, isjson);
 	if (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 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 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 Comms Error", &(cgpu->dev_comms_error_count), false);
+	root = api_add_int(root, "*Dev Throttle", &(cgpu->dev_throttle_count), false);
 
 
 	if (isjson && (device > 0))
 	if (isjson && (device > 0))
 		strcat(io_buffer, COMMA);
 		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 Can Roll", &(pool_stats->canroll), false);
 		root = api_add_bool(root, "Work Had Expire", &(pool_stats->hadexpire), 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_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)
 	if (extra)
@@ -2795,6 +2865,10 @@ static void debugstate(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __
 		opt_quiet = false;
 		opt_quiet = false;
 		opt_protocol = false;
 		opt_protocol = false;
 		want_per_device_stats = false;
 		want_per_device_stats = false;
+		opt_worktime = false;
+		break;
+	case 'w':
+		opt_worktime ^= true;
 		break;
 		break;
 	default:
 	default:
 		// anything else just reports the settings
 		// 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, "Debug", &opt_debug, false);
 	root = api_add_bool(root, "RPCProto", &opt_protocol, 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, "PerDevice", &want_per_device_stats, false);
+	root = api_add_bool(root, "WorkTime", &opt_worktime, false);
 
 
 	root = print_data(root, buf, isjson);
 	root = print_data(root, buf, isjson);
 	if (isjson)
 	if (isjson)
@@ -2879,6 +2954,7 @@ struct CMDS {
 	{ "pga",		pgadev,		false },
 	{ "pga",		pgadev,		false },
 	{ "pgaenable",		pgaenable,	true },
 	{ "pgaenable",		pgaenable,	true },
 	{ "pgadisable",		pgadisable,	true },
 	{ "pgadisable",		pgadisable,	true },
+	{ "pgaidentify",	pgaidentify,	true },
 #endif
 #endif
 #ifdef WANT_CPUMINE
 #ifdef WANT_CPUMINE
 	{ "cpu",		cpudev,		false },
 	{ "cpu",		cpudev,		false },

+ 1 - 1
compat.h

@@ -57,7 +57,7 @@ enum {
 	PRIO_PROCESS		= 0,
 	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);
 	return -!SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
 }
 }

+ 57 - 5
driver-bitforce.c

@@ -19,8 +19,8 @@
 #include "config.h"
 #include "config.h"
 
 
 #include "compat.h"
 #include "compat.h"
-#include "fpgautils.h"
 #include "miner.h"
 #include "miner.h"
+#include "fpgautils.h"
 
 
 #define BITFORCE_SLEEP_MS 500
 #define BITFORCE_SLEEP_MS 500
 #define BITFORCE_TIMEOUT_S 7
 #define BITFORCE_TIMEOUT_S 7
@@ -76,6 +76,7 @@ static bool bitforce_detect_one(const char *devpath)
 	}
 	}
 
 
 	BFwrite(fdDev, "ZGX", 3);
 	BFwrite(fdDev, "ZGX", 3);
+	pdevbuf[0] = '\0';
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	if (unlikely(!pdevbuf[0])) {
 	if (unlikely(!pdevbuf[0])) {
 		applog(LOG_DEBUG, "BFL: Error reading/timeout (ZGX)");
 		applog(LOG_DEBUG, "BFL: Error reading/timeout (ZGX)");
@@ -125,7 +126,7 @@ static int bitforce_detect_auto(void)
 
 
 static void bitforce_detect(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)
 static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
@@ -205,6 +206,7 @@ void bitforce_init(struct cgpu_info *bitforce)
 
 
 	do {
 	do {
 		BFwrite(fdDev, "ZGX", 3);
 		BFwrite(fdDev, "ZGX", 3);
+		pdevbuf[0] = '\0';
 		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 
 
 		if (unlikely(!pdevbuf[0])) {
 		if (unlikely(!pdevbuf[0])) {
@@ -234,6 +236,37 @@ void bitforce_init(struct cgpu_info *bitforce)
 	mutex_unlock(&bitforce->device_mutex);
 	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)
 static bool bitforce_get_temp(struct cgpu_info *bitforce)
 {
 {
 	int fdDev = bitforce->device_fd;
 	int fdDev = bitforce->device_fd;
@@ -244,16 +277,23 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 		return false;
 		return false;
 
 
 	/* Do not try to get the temperature if we're polling for a result to
 	/* 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)
 	if (bitforce->polling)
 		return true;
 		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 */
 	 * can't grab the mutex here */
 	if (mutex_trylock(&bitforce->device_mutex))
 	if (mutex_trylock(&bitforce->device_mutex))
 		return false;
 		return false;
 
 
 	BFwrite(fdDev, "ZLX", 3);
 	BFwrite(fdDev, "ZLX", 3);
+	pdevbuf[0] = '\0';
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	mutex_unlock(&bitforce->device_mutex);
 	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
 		 * our responses are out of sync and flush the buffer to
 		 * hopefully recover */
 		 * hopefully recover */
 		applog(LOG_WARNING, "BFL%i: Garbled response probably throttling, clearing buffer", bitforce->device_id);
 		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 */
 		/* Count throttling episodes as hardware errors */
 		bitforce->hw_errors++;
 		bitforce->hw_errors++;
 		bitforce_clear_buffer(bitforce);
 		bitforce_clear_buffer(bitforce);
-		return false;;
+		return false;
 	}
 	}
 
 
 	return true;
 	return true;
@@ -307,6 +350,7 @@ re_send:
 		BFwrite(fdDev, "ZPX", 3);
 		BFwrite(fdDev, "ZPX", 3);
 	else
 	else
 		BFwrite(fdDev, "ZDX", 3);
 		BFwrite(fdDev, "ZDX", 3);
+	pdevbuf[0] = '\0';
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	if (!pdevbuf[0] || !strncasecmp(pdevbuf, "B", 1)) {
 	if (!pdevbuf[0] || !strncasecmp(pdevbuf, "B", 1)) {
 		mutex_unlock(&bitforce->device_mutex);
 		mutex_unlock(&bitforce->device_mutex);
@@ -347,6 +391,7 @@ re_send:
 		BFwrite(fdDev, ob, 68);
 		BFwrite(fdDev, ob, 68);
 	}
 	}
 
 
+	pdevbuf[0] = '\0';
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	mutex_unlock(&bitforce->device_mutex);
 	mutex_unlock(&bitforce->device_mutex);
 
 
@@ -397,6 +442,7 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 	while (1) {
 	while (1) {
 		mutex_lock(&bitforce->device_mutex);
 		mutex_lock(&bitforce->device_mutex);
 		BFwrite(fdDev, "ZFX", 3);
 		BFwrite(fdDev, "ZFX", 3);
+		pdevbuf[0] = '\0';
 		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 		mutex_unlock(&bitforce->device_mutex);
 		mutex_unlock(&bitforce->device_mutex);
 
 
@@ -554,6 +600,11 @@ static bool bitforce_get_stats(struct cgpu_info *bitforce)
 	return bitforce_get_temp(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)
 static bool bitforce_thread_init(struct thr_info *thr)
 {
 {
 	struct cgpu_info *bitforce = thr->cgpu;
 	struct cgpu_info *bitforce = thr->cgpu;
@@ -590,6 +641,7 @@ struct device_api bitforce_api = {
 	.reinit_device = bitforce_init,
 	.reinit_device = bitforce_init,
 	.get_statline_before = get_bitforce_statline_before,
 	.get_statline_before = get_bitforce_statline_before,
 	.get_stats = bitforce_get_stats,
 	.get_stats = bitforce_get_stats,
+	.identify_device = bitforce_identify,
 	.thread_prepare = bitforce_thread_prepare,
 	.thread_prepare = bitforce_thread_prepare,
 	.thread_init = bitforce_thread_init,
 	.thread_init = bitforce_thread_init,
 	.scanhash = bitforce_scanhash,
 	.scanhash = bitforce_scanhash,

+ 1 - 1
driver-cairnsmore.c

@@ -51,7 +51,7 @@ static int cairnsmore_detect_auto(void)
 static void cairnsmore_detect()
 static void cairnsmore_detect()
 {
 {
 	// Actual serial detection is handled by Icarus driver
 	// 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)
 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 */
 /* 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 char *set_int_range(const char *arg, int *i, int min, int max);
 extern int dev_from_id(int thr_id);
 extern int dev_from_id(int thr_id);
 
 
@@ -834,7 +834,7 @@ CPUSearch:
 	/* if nonce found, submit work */
 	/* if nonce found, submit work */
 	if (unlikely(rc)) {
 	if (unlikely(rc)) {
 		applog(LOG_DEBUG, "CPU %d found something?", dev_from_id(thr_id));
 		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);
 			applog(LOG_ERR, "Failed to submit_work_sync in miner_thread %d", thr_id);
 		}
 		}
 		work->blk.nonce = last_nonce + 1;
 		work->blk.nonce = last_nonce + 1;

+ 8 - 4
driver-icarus.c

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

+ 2 - 2
driver-modminer.c

@@ -13,9 +13,9 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <unistd.h>
 
 
-#include "fpgautils.h"
 #include "logging.h"
 #include "logging.h"
 #include "miner.h"
 #include "miner.h"
+#include "fpgautils.h"
 
 
 #define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.ncd"
 #define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.ncd"
 #define BISTREAM_USER_ID "\2\4$B"
 #define BISTREAM_USER_ID "\2\4$B"
@@ -138,7 +138,7 @@ modminer_detect_auto()
 static void
 static void
 modminer_detect()
 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__);
 #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()
 static void ztex_detect()
 {
 {
 	// This wrapper ensures users can specify -S ztex:noauto to disable it
 	// 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)
 static bool ztex_updateFreq(struct libztex_device* ztex)

+ 26 - 31
fpgautils.c

@@ -51,13 +51,12 @@ enum {
 #endif
 #endif
 
 
 #include "elist.h"
 #include "elist.h"
-#include "fpgautils.h"
 #include "logging.h"
 #include "logging.h"
 #include "miner.h"
 #include "miner.h"
+#include "fpgautils.h"
 
 
 #ifdef HAVE_LIBUDEV
 #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 *udev = udev_new();
 	struct udev_enumerate *enumerate = udev_enumerate_new(udev);
 	struct udev_enumerate *enumerate = udev_enumerate_new(udev);
@@ -87,15 +86,13 @@ serial_autodetect_udev(detectone_func_t detectone, const char*prodname)
 	return found;
 	return found;
 }
 }
 #else
 #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;
 	return 0;
 }
 }
 #endif
 #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
 #ifndef WIN32
 	DIR *D;
 	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);
 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;
 	struct string_elist *iter, *tmp;
-	const char*s, *p;
+	const char *dev, *colon;
 	bool inhibitauto = false;
 	bool inhibitauto = false;
 	char found = 0;
 	char found = 0;
 	bool forceauto = flags & 1;
 	bool forceauto = flags & 1;
 	bool hasname;
 	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) {
 	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;
 				continue;
-			s = p + 1;
+
+			dev = colon + 1;
 			hasname = true;
 			hasname = true;
 		}
 		}
 		else
 		else
 			hasname = false;
 			hasname = false;
-		if (!strcmp(s, "auto"))
+		if (!strcmp(dev, "auto"))
 			forceauto = true;
 			forceauto = true;
-		else
-		if (!strcmp(s, "noauto"))
+		else if (!strcmp(dev, "noauto"))
 			inhibitauto = true;
 			inhibitauto = true;
 		else
 		else
 		if ((flags & 2) && !hasname)
 		if ((flags & 2) && !hasname)
@@ -242,13 +242,12 @@ _serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t aut
 		if (!detectone)
 		if (!detectone)
 		{}  // do nothing
 		{}  // do nothing
 		else
 		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);
 			string_elist_del(iter);
 		}
 		}
-		else
-		if (detectone(s)) {
+		else if (detectone(dev)) {
 			string_elist_del(iter);
 			string_elist_del(iter);
 			inhibitauto = true;
 			inhibitauto = true;
 			++found;
 			++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
 /* NOTE: Linux only supports uint8_t (decisecond) timeouts; limiting it in
  *       this interface buys us warnings when bad constants are passed 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
 #ifdef WIN32
 	HANDLE hSerial = CreateFile(devpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
 	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
 #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;
 	ssize_t len, tlen = 0;
 	while (bufsiz) {
 	while (bufsiz) {
@@ -617,8 +614,7 @@ _serial_read(int fd, char *buf, size_t bufsiz, char *eol)
 	return tlen;
 	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];
 	char fullpath[PATH_MAX];
 	strcpy(fullpath, path);
 	strcpy(fullpath, path);
@@ -642,8 +638,7 @@ _open_bitstream(const char*path, const char*subdir, const char*filename)
 	_open_bitstream(path, NULL);  \
 	_open_bitstream(path, NULL);  \
 } while(0)
 } while(0)
 
 
-FILE*
-open_bitstream(const char*dname, const char*filename)
+FILE *open_bitstream(const char *dname, const char *filename)
 {
 {
 	FILE *f;
 	FILE *f;
 
 

+ 17 - 15
fpgautils.h

@@ -15,34 +15,36 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <unistd.h>
 
 
+struct device_api;
+
 typedef bool(*detectone_func_t)(const char*);
 typedef bool(*detectone_func_t)(const char*);
 typedef int(*autoscan_func_t)();
 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_devserial(detectone_func_t, const char*prodname);
 extern int serial_autodetect_udev     (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 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 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)  \
 #define serial_read(fd, buf, count)  \
 	_serial_read(fd, (char*)(buf), count, NULL)
 	_serial_read(fd, (char*)(buf), count, NULL)
 #define serial_read_line(fd, buf, bufsiz, eol)  \
 #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)
 #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
 #endif

+ 1 - 1
libztex.c

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

+ 174 - 50
miner.c

@@ -159,6 +159,7 @@ bool opt_delaynet;
 bool opt_disable_pool = true;
 bool opt_disable_pool = true;
 char *opt_icarus_options = NULL;
 char *opt_icarus_options = NULL;
 char *opt_icarus_timing = NULL;
 char *opt_icarus_timing = NULL;
+bool opt_worktime;
 
 
 char *opt_kernel_path;
 char *opt_kernel_path;
 char *cgminer_path;
 char *cgminer_path;
@@ -209,6 +210,7 @@ int hw_errors;
 int total_accepted, total_rejected, total_diff1;
 int total_accepted, total_rejected, total_diff1;
 float total_accepted_weighed;
 float total_accepted_weighed;
 int total_getworks, total_stale, total_discarded;
 int total_getworks, total_stale, total_discarded;
+double total_diff_accepted, total_diff_rejected, total_diff_stale;
 static int total_queued, staged_rollable;
 static int total_queued, staged_rollable;
 unsigned int new_blocks;
 unsigned int new_blocks;
 unsigned int found_blocks;
 unsigned int found_blocks;
@@ -368,7 +370,7 @@ static void sharelog(const char*disposition, const struct work*work)
 	thr_id = work->thr_id;
 	thr_id = work->thr_id;
 	cgpu = thr_info[thr_id].cgpu;
 	cgpu = thr_info[thr_id].cgpu;
 	pool = work->pool;
 	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));
 	target = bin2hex(work->target, sizeof(work->target));
 	if (unlikely(!target)) {
 	if (unlikely(!target)) {
 		applog(LOG_ERR, "sharelog target OOM");
 		applog(LOG_ERR, "sharelog target OOM");
@@ -1195,6 +1197,9 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITH_ARG("--userpass|-O",
 	OPT_WITH_ARG("--userpass|-O",
 		     set_userpass, NULL, NULL,
 		     set_userpass, NULL, NULL,
 		     "Username:Password pair for bitcoin JSON-RPC server"),
 		     "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_WITH_ARG("--pools",
 			opt_set_bool, NULL, NULL, opt_hidden),
 			opt_set_bool, NULL, NULL, opt_hidden),
 	OPT_ENDTABLE
 	OPT_ENDTABLE
@@ -1449,8 +1454,6 @@ static void calc_midstate(struct work *work)
 
 
 static bool work_decode(const json_t *val, struct work *work)
 static bool work_decode(const json_t *val, struct work *work)
 {
 {
-	unsigned char bits = 0, i;
-	
 	if (unlikely(detect_algo == 1)) {
 	if (unlikely(detect_algo == 1)) {
 		json_t *tmp = json_object_get(val, "algorithm");
 		json_t *tmp = json_object_get(val, "algorithm");
 		const char *v = tmp ? json_string_value(tmp) : "";
 		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;
 			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));
 	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;
 	struct pool *pool = work->pool;
 	int rolltime;
 	int rolltime;
 	uint32_t *hash32;
 	uint32_t *hash32;
+	struct timeval tv_submit, tv_submit_reply;
 	char hashshow[64+1] = "";
 	char hashshow[64+1] = "";
+	char worktime[200] = "";
 
 
 	if (work->tmpl) {
 	if (work->tmpl) {
 		unsigned char data[76];
 		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);
 	applog(LOG_DEBUG, "DBG: sending %s submit RPC call: %s", pool->rpc_url, sd);
 
 
+	gettimeofday(&tv_submit, NULL);
 	/* issue JSON-RPC request */
 	/* issue JSON-RPC request */
 	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, false, false, &rolltime, pool, true);
 	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, false, false, &rolltime, pool, true);
 
 
 	free(s);
 	free(s);
 	free(sd);
 	free(sd);
 
 
+	gettimeofday(&tv_submit_reply, NULL);
 	if (unlikely(!val)) {
 	if (unlikely(!val)) {
 		applog(LOG_INFO, "submit_upstream_work json_rpc_call failed");
 		applog(LOG_INFO, "submit_upstream_work json_rpc_call failed");
 		if (!pool_tset(pool, &pool->submit_fail)) {
 		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)
 		if (opt_scrypt)
 			sprintf(hashshow, "%08lx.%08lx", (unsigned long)(hash32[7]), (unsigned long)(hash32[6]));
 			sprintf(hashshow, "%08lx.%08lx", (unsigned long)(hash32[7]), (unsigned long)(hash32[6]));
 		else {
 		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!" : "");
 				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
 	/* 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 */
 	 * same time is zero so there is no point adding extra locking */
 	if (json_is_null(res) || json_is_true(res)) {
 	if (json_is_null(res) || json_is_true(res)) {
 		cgpu->accepted++;
 		cgpu->accepted++;
-		cgpu->accepted_weighed += work->difficulty;
+		cgpu->accepted_weighed += work->work_difficulty;
 		total_accepted++;
 		total_accepted++;
-		total_accepted_weighed += work->difficulty;
+		total_accepted_weighed += work->work_difficulty;
 		pool->accepted++;
 		pool->accepted++;
+		cgpu->diff_accepted += work->work_difficulty;
+		total_diff_accepted += work->work_difficulty;
+		pool->diff_accepted += work->work_difficulty;
 		pool->seq_rejects = 0;
 		pool->seq_rejects = 0;
 		cgpu->last_share_pool = pool->pool_no;
 		cgpu->last_share_pool = pool->pool_no;
 		cgpu->last_share_pool_time = time(NULL);
 		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_time = cgpu->last_share_pool_time;
+		pool->last_share_diff = work->work_difficulty;
 		applog(LOG_DEBUG, "PROOF OF WORK RESULT: true (yay!!!)");
 		applog(LOG_DEBUG, "PROOF OF WORK RESULT: true (yay!!!)");
 		if (!QUIET) {
 		if (!QUIET) {
 			if (total_pools > 1)
 			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
 			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);
 		sharelog("accept", work);
 		if (opt_shares && total_accepted >= opt_shares) {
 		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++;
 		cgpu->rejected++;
 		total_rejected++;
 		total_rejected++;
 		pool->rejected++;
 		pool->rejected++;
+		cgpu->diff_rejected += work->work_difficulty;
+		total_diff_rejected += work->work_difficulty;
+		pool->diff_rejected += work->work_difficulty;
 		pool->seq_rejects++;
 		pool->seq_rejects++;
 		applog(LOG_DEBUG, "PROOF OF WORK RESULT: false (booooo)");
 		applog(LOG_DEBUG, "PROOF OF WORK RESULT: false (booooo)");
 		if (!QUIET) {
 		if (!QUIET) {
@@ -2227,8 +2269,8 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub
 			} else
 			} else
 				strcpy(reason, "");
 				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);
 			sharelog(disposition, work);
 		}
 		}
 
 
@@ -2329,6 +2371,42 @@ static inline struct pool *select_pool(bool lagging)
 	return pool;
 	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)
 static void get_benchmark_work(struct work *work)
 {
 {
 	// Use a random work block pulled from a pool
 	// 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);
 	memcpy(work, &bench_block, min_size);
 	work->mandatory = true;
 	work->mandatory = true;
 	work->pool = pools[0];
 	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)
 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 pool *pool = work->pool;
 	struct cgminer_pool_stats *pool_stats = &(pool->cgminer_pool_stats);
 	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;
 	json_t *val = NULL;
 	bool rc = false;
 	bool rc = false;
 	char *url;
 	char *url;
@@ -2428,7 +2510,7 @@ tryagain:
 
 
 	url = pool->rpc_url;
 	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,
 	val = json_rpc_call(curl, url, pool->rpc_userpass, rpc_req, false,
 			    false, &work->rolltime, pool, false);
 			    false, &work->rolltime, pool, false);
@@ -2447,8 +2529,8 @@ tryagain:
 	} else
 	} else
 		applog(LOG_DEBUG, "Failed json_rpc_call in get_upstream_work");
 		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 += ((double)tv_elapsed.tv_sec + ((double)tv_elapsed.tv_usec / 1000000)) * 0.63;
 	pool_stats->getwork_wait_rolling /= 1.63;
 	pool_stats->getwork_wait_rolling /= 1.63;
 
 
@@ -2465,6 +2547,8 @@ tryagain:
 
 
 	work->pool = pool;
 	work->pool = pool;
 	work->longpoll = false;
 	work->longpoll = false;
+	work->getwork_mode = GETWORK_MODE_POOL;
+	calc_diff(work);
 	total_getworks++;
 	total_getworks++;
 	pool->getwork_requested++;
 	pool->getwork_requested++;
 
 
@@ -2818,6 +2902,7 @@ static struct work *make_clone(struct work *work)
 
 
 	workcpy(work_clone, work);
 	workcpy(work_clone, work);
 	work_clone->clone = true;
 	work_clone->clone = true;
+	gettimeofday((struct timeval *)&(work_clone->tv_cloned), NULL);
 	work_clone->longpoll = false;
 	work_clone->longpoll = false;
 	work_clone->mandatory = false;
 	work_clone->mandatory = false;
 	/* Make cloned work appear slightly older to bias towards keeping the
 	/* Make cloned work appear slightly older to bias towards keeping the
@@ -2862,6 +2947,15 @@ out:
 
 
 static bool queue_request(void);
 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)
 static void *get_work_thread(void *userdata)
 {
 {
 	struct workio_cmd *wc = (struct workio_cmd *)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 */
 		/* obtain new work from bitcoin via JSON-RPC */
 		if (!get_upstream_work(ret_work, ce->curl)) {
 		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");
 			applog(LOG_DEBUG, "json_rpc_call failed on get work, retrying");
 			dec_queued(pool);
 			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();
 			queue_request();
 			free_work(ret_work);
 			free_work(ret_work);
 			goto out;
 			goto out;
 		}
 		}
+		pool->seq_getfails = 0;
 
 
 		ret_work->queued = true;
 		ret_work->queued = true;
 	}
 	}
@@ -3030,6 +3128,8 @@ static void submit_discard_share(struct work *work)
 	sharelog("discard", work);
 	sharelog("discard", work);
 	++total_stale;
 	++total_stale;
 	++(work->pool->stale_shares);
 	++(work->pool->stale_shares);
+	total_diff_stale += work->work_difficulty;
+	work->pool->diff_stale += work->work_difficulty;
 }
 }
 
 
 static void *submit_work_thread(void *userdata)
 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(" Share submissions: %d\n", pool->accepted + pool->rejected);
 		wlog(" Accepted shares: %d\n", pool->accepted);
 		wlog(" Accepted shares: %d\n", pool->accepted);
 		wlog(" Rejected shares: %d\n", pool->rejected);
 		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)
 		if (pool->accepted || pool->rejected)
 			wlog(" Reject ratio: %.1f%%\n", (double)(pool->rejected * 100) / (double)(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;
 		efficiency = pool->getwork_requested ? pool->accepted * 100.0 / pool->getwork_requested : 0.0;
@@ -4093,12 +4195,13 @@ static void display_options(void)
 	clear_logwin();
 	clear_logwin();
 retry:
 retry:
 	wlogprint("[N]ormal [C]lear [S]ilent mode (disable all output)\n");
 	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",
 		opt_debug_console ? "on" : "off",
 	        want_per_device_stats? "on" : "off",
 	        want_per_device_stats? "on" : "off",
 		opt_quiet ? "on" : "off",
 		opt_quiet ? "on" : "off",
 		opt_log_output ? "on" : "off",
 		opt_log_output ? "on" : "off",
 		opt_protocol ? "on" : "off",
 		opt_protocol ? "on" : "off",
+		opt_worktime ? "on" : "off",
 		opt_log_interval);
 		opt_log_interval);
 	wlogprint("Select an option or any other key to return\n");
 	wlogprint("Select an option or any other key to return\n");
 	input = getch();
 	input = getch();
@@ -4152,6 +4255,10 @@ retry:
 		goto retry;
 		goto retry;
 	} else if (!strncasecmp(&input, "s", 1)) {
 	} else if (!strncasecmp(&input, "s", 1)) {
 		opt_realquiet = true;
 		opt_realquiet = true;
+	} else if (!strncasecmp(&input, "w", 1)) {
+		opt_worktime ^= true;
+		wlogprint("WorkTime details %s\n", opt_worktime ? "enabled" : "disabled");
+		goto retry;
 	} else
 	} else
 		clear_logwin();
 		clear_logwin();
 
 
@@ -4557,6 +4664,7 @@ static char *absolute_uri(char *uri, const char *ref)
 
 
 static bool pool_active(struct pool *pool, bool pinging)
 static bool pool_active(struct pool *pool, bool pinging)
 {
 {
+	struct timeval tv_getwork, tv_getwork_reply;
 	bool ret = false;
 	bool ret = false;
 	json_t *val;
 	json_t *val;
 	CURL *curl;
 	CURL *curl;
@@ -4581,8 +4689,10 @@ tryagain:
 		return false;
 		return false;
 
 
 	applog(LOG_INFO, "Testing pool %s", pool->rpc_url);
 	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,
 	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, rpc_req,
 			true, false, &rolltime, pool, false);
 			true, false, &rolltime, pool, false);
+	gettimeofday(&tv_getwork_reply, NULL);
 
 
 	if (val) {
 	if (val) {
 		bool rc;
 		bool rc;
@@ -4598,6 +4708,10 @@ tryagain:
 			       pool->pool_no, pool->rpc_url);
 			       pool->pool_no, pool->rpc_url);
 			work->pool = pool;
 			work->pool = pool;
 			work->rolltime = rolltime;
 			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");
 			applog(LOG_DEBUG, "Pushing pooltest work to base pool");
 
 
 			tq_push(thr_info[stage_thr_id].q, work);
 			tq_push(thr_info[stage_thr_id].q, work);
@@ -4662,15 +4776,6 @@ out:
 	return ret;
 	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)
 static inline int cp_prio(void)
 {
 {
 	int prio;
 	int prio;
@@ -4897,7 +5002,7 @@ out:
 	work->mined = true;
 	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;
 	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->cmd = WC_SUBMIT_WORK;
 	wc->thr = thr;
 	wc->thr = thr;
 	workcpy(wc->work, work_in);
 	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");
 	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)
 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++;
 	total_diff1++;
 	thr->cgpu->diff1++;
 	thr->cgpu->diff1++;
 	work->pool->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);
 		applog(LOG_INFO, "Pool %d share below target", work->pool->pool_no);
 		return true;
 		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)
 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;
 	mythr->rolling = mythr->cgpu->rolling = 0;
 	applog(LOG_DEBUG, "Popping wakeup ping in miner thread");
 	applog(LOG_DEBUG, "Popping wakeup ping in miner thread");
 	thread_reportout(mythr);
 	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);
 	thread_reportin(mythr);
 	applog(LOG_WARNING, "Thread %d being re-enabled", thr_id);
 	applog(LOG_WARNING, "Thread %d being re-enabled", thr_id);
 	if (api->thread_enable)
 	if (api->thread_enable)
@@ -5103,6 +5214,8 @@ void *miner_thread(void *userdata)
 			}
 			}
 			pool_stats->getwork_calls++;
 			pool_stats->getwork_calls++;
 
 
+			gettimeofday(&(work->tv_work_start), NULL);
+
 			thread_reportin(mythr);
 			thread_reportin(mythr);
 			hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce);
 			hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce);
 			thread_reportin(mythr);
 			thread_reportin(mythr);
@@ -5199,7 +5312,7 @@ enum {
 };
 };
 
 
 /* Stage another work item from the work returned in a longpoll */
 /* 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;
 	bool rc;
 
 
@@ -5212,6 +5325,10 @@ static void convert_to_work(json_t *val, int rolltime, struct pool *pool, struct
 	work->pool = pool;
 	work->pool = pool;
 	work->rolltime = rolltime;
 	work->rolltime = rolltime;
 	work->longpoll = true;
 	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)
 	if (pool->enabled == POOL_REJECTING)
 		work->mandatory = true;
 		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
 	/* This *pool is the source of the actual longpoll, not the pool we've
 	 * tied it to */
 	 * tied it to */
 	struct pool *pool = NULL;
 	struct pool *pool = NULL;
-	struct timeval start, end;
+	struct timeval start, reply, end;
 	CURL *curl = NULL;
 	CURL *curl = NULL;
 	int failures = 0;
 	int failures = 0;
 	int rolltime;
 	int rolltime;
@@ -5344,13 +5461,16 @@ retry_pool:
 		val = json_rpc_call(curl, pool->lp_url, pool->rpc_userpass, rpc_req,
 		val = json_rpc_call(curl, pool->lp_url, pool->rpc_userpass, rpc_req,
 				    false, true, &rolltime, pool, false);
 				    false, true, &rolltime, pool, false);
 		pool->lp_socket = CURL_SOCKET_BAD;
 		pool->lp_socket = CURL_SOCKET_BAD;
+
+		gettimeofday(&reply, NULL);
+
 		if (likely(val)) {
 		if (likely(val)) {
 			soval = json_object_get(json_object_get(val, "result"), "submitold");
 			soval = json_object_get(json_object_get(val, "result"), "submitold");
 			if (soval)
 			if (soval)
 				pool->submit_old = json_is_true(soval);
 				pool->submit_old = json_is_true(soval);
 			else
 			else
 				pool->submit_old = false;
 				pool->submit_old = false;
-			convert_to_work(val, rolltime, pool, work);
+			convert_to_work(val, rolltime, pool, work, &start, &reply);
 			failures = 0;
 			failures = 0;
 			json_decref(val);
 			json_decref(val);
 		} else {
 		} else {
@@ -5726,6 +5846,8 @@ static void print_summary(void)
 	applog(LOG_WARNING, "Share submissions: %d", total_accepted + total_rejected);
 	applog(LOG_WARNING, "Share submissions: %d", total_accepted + total_rejected);
 	applog(LOG_WARNING, "Accepted shares: %d", total_accepted);
 	applog(LOG_WARNING, "Accepted shares: %d", total_accepted);
 	applog(LOG_WARNING, "Rejected shares: %d", total_rejected);
 	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)
 	if (total_accepted || total_rejected)
 		applog(LOG_WARNING, "Reject ratio: %.1f%%", (double)(total_rejected * 100) / (double)(total_accepted + total_rejected));
 		applog(LOG_WARNING, "Reject ratio: %.1f%%", (double)(total_rejected * 100) / (double)(total_accepted + total_rejected));
 	applog(LOG_WARNING, "Hardware errors: %d", hw_errors);
 	applog(LOG_WARNING, "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, " Share submissions: %d", pool->accepted + pool->rejected);
 			applog(LOG_WARNING, " Accepted shares: %d", pool->accepted);
 			applog(LOG_WARNING, " Accepted shares: %d", pool->accepted);
 			applog(LOG_WARNING, " Rejected shares: %d", pool->rejected);
 			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)
 			if (pool->accepted || pool->rejected)
 				applog(LOG_WARNING, " Reject ratio: %.1f%%", (double)(pool->rejected * 100) / (double)(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;
 			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_extra_device_status)(struct cgpu_info*);
 	struct api_data *(*get_api_stats)(struct cgpu_info*);
 	struct api_data *(*get_api_stats)(struct cgpu_info*);
 	bool (*get_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
 	// Thread-specific functions
 	bool (*thread_prepare)(struct thr_info*);
 	bool (*thread_prepare)(struct thr_info*);
@@ -315,6 +316,7 @@ enum dev_reason {
 	REASON_DEV_OVER_HEAT,
 	REASON_DEV_OVER_HEAT,
 	REASON_DEV_THERMAL_CUTOFF,
 	REASON_DEV_THERMAL_CUTOFF,
 	REASON_DEV_COMMS_ERROR,
 	REASON_DEV_COMMS_ERROR,
+	REASON_DEV_THROTTLE,
 };
 };
 
 
 #define REASON_NONE			"None"
 #define REASON_NONE			"None"
@@ -327,6 +329,7 @@ enum dev_reason {
 #define REASON_DEV_OVER_HEAT_STR	"Device over heated"
 #define REASON_DEV_OVER_HEAT_STR	"Device over heated"
 #define REASON_DEV_THERMAL_CUTOFF_STR	"Device reached thermal cutoff"
 #define REASON_DEV_THERMAL_CUTOFF_STR	"Device reached thermal cutoff"
 #define REASON_DEV_COMMS_ERROR_STR	"Device comms error"
 #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 REASON_UNKNOWN_STR		"Unknown reason - code bug"
 
 
 #define MIN_SEC_UNSET 99999999
 #define MIN_SEC_UNSET 99999999
@@ -350,6 +353,11 @@ struct cgminer_pool_stats {
 	bool canroll;
 	bool canroll;
 	bool hadexpire;
 	bool hadexpire;
 	uint32_t rolltime;
 	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 {
 struct cgpu_info {
@@ -375,6 +383,7 @@ struct cgpu_info {
 	uint32_t nonces;
 	uint32_t nonces;
 	bool nonce_range;
 	bool nonce_range;
 	bool polling;
 	bool polling;
+	bool flash_led;
 #endif
 #endif
 	void *cgpu_data;
 	void *cgpu_data;
 	pthread_mutex_t		device_mutex;
 	pthread_mutex_t		device_mutex;
@@ -439,8 +448,11 @@ struct cgpu_info {
 	float gpu_vddc;
 	float gpu_vddc;
 #endif
 #endif
 	int diff1;
 	int diff1;
+	double diff_accepted;
+	double diff_rejected;
 	int last_share_pool;
 	int last_share_pool;
 	time_t last_share_pool_time;
 	time_t last_share_pool_time;
+	double last_share_diff;
 
 
 	time_t device_last_well;
 	time_t device_last_well;
 	time_t device_last_not_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_over_heat_count;	// It's a warning but worth knowing
 	int dev_thermal_cutoff_count;
 	int dev_thermal_cutoff_count;
 	int dev_comms_error_count;
 	int dev_comms_error_count;
+	int dev_throttle_count;
 
 
 	struct cgminer_stats cgminer_stats;
 	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 thr_info_freeze(struct thr_info *thr);
 extern void nmsleep(unsigned int msecs);
 extern void nmsleep(unsigned int msecs);
 extern void rename_thr(const char* name);
 extern void rename_thr(const char* name);
+extern double tdiff(struct timeval *end, struct timeval *start);
 
 
 struct string_elist {
 struct string_elist {
 	char *string;
 	char *string;
@@ -637,6 +651,7 @@ extern bool opt_delaynet;
 extern bool opt_restart;
 extern bool opt_restart;
 extern char *opt_icarus_options;
 extern char *opt_icarus_options;
 extern char *opt_icarus_timing;
 extern char *opt_icarus_timing;
+extern bool opt_worktime;
 #ifdef USE_BITFORCE
 #ifdef USE_BITFORCE
 extern bool opt_bfl_noncerange;
 extern bool opt_bfl_noncerange;
 #endif
 #endif
@@ -738,6 +753,7 @@ extern unsigned int new_blocks;
 extern unsigned int found_blocks;
 extern unsigned int found_blocks;
 extern int total_accepted, total_rejected, total_diff1;;
 extern int total_accepted, total_rejected, total_diff1;;
 extern int total_getworks, total_stale, total_discarded;
 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 local_work;
 extern unsigned int total_go, total_ro;
 extern unsigned int total_go, total_ro;
 extern const int opt_cutofftemp;
 extern const int opt_cutofftemp;
@@ -806,9 +822,14 @@ struct pool {
 	int prio;
 	int prio;
 	int accepted, rejected;
 	int accepted, rejected;
 	int seq_rejects;
 	int seq_rejects;
+	int seq_getfails;
 	int solved;
 	int solved;
 	int diff1;
 	int diff1;
 
 
+	double diff_accepted;
+	double diff_rejected;
+	double diff_stale;
+
 	int queued;
 	int queued;
 	int staged;
 	int staged;
 
 
@@ -860,11 +881,17 @@ struct pool {
 	struct list_head curlring;
 	struct list_head curlring;
 
 
 	time_t last_share_time;
 	time_t last_share_time;
+	double last_share_diff;
 
 
 	struct cgminer_stats cgminer_stats;
 	struct cgminer_stats cgminer_stats;
 	struct cgminer_pool_stats cgminer_pool_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 {
 struct work {
 	unsigned char	data[128];
 	unsigned char	data[128];
 	unsigned char	hash1[64];
 	unsigned char	hash1[64];
@@ -897,13 +924,18 @@ struct work {
 	int		id;
 	int		id;
 	UT_hash_handle hh;
 	UT_hash_handle hh;
 	
 	
-	float		difficulty;
-
-	time_t share_found_time;
+	double		work_difficulty;
 
 
 	blktemplate_t	*tmpl;
 	blktemplate_t	*tmpl;
 	int		*tmpl_refcount;
 	int		*tmpl_refcount;
 	unsigned int	dataid;
 	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 *);
 extern void get_datestamp(char *, struct timeval *);
@@ -950,6 +982,7 @@ enum api_data_type {
 	API_FREQ,
 	API_FREQ,
 	API_VOLTS,
 	API_VOLTS,
 	API_HS,
 	API_HS,
+	API_DIFF,
 	API_JSON,
 	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_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_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_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);
 extern struct api_data *api_add_json(struct api_data *root, char *name, json_t *data, bool copy_data);
 
 
 #endif /* __MINER_H__ */
 #endif /* __MINER_H__ */

+ 34 - 7
miner.php

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

+ 6 - 0
util.c

@@ -766,3 +766,9 @@ void rename_thr(const char* name) {
 	(void)name;
 	(void)name;
 #endif
 #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;
+}