Browse Source

Merge pull request #310 from kanoi/master

API debug, API-README, API Correct diff1 name, Device diff1, API diff1 etc.
Con Kolivas 13 years ago
parent
commit
3243b91e2d
15 changed files with 869 additions and 151 deletions
  1. 77 6
      API-README
  2. 66 6
      README
  3. 227 7
      api.c
  4. 190 40
      cgminer.c
  5. 58 6
      driver-bitforce.c
  6. 2 2
      driver-cpu.c
  7. 8 4
      driver-icarus.c
  8. 7 9
      driver-modminer.c
  9. 14 2
      findnonce.c
  10. 24 29
      fpgautils.c
  11. 13 13
      fpgautils.h
  12. 2 0
      logging.h
  13. 40 4
      miner.h
  14. 69 22
      miner.php
  15. 72 1
      util.c

+ 77 - 6
API-README

@@ -124,7 +124,10 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               Log Interval=N, <- log interval (--log N)
                               Device Code=GPU ICA , <- spaced list of compiled devices
                               OS=Linux/Apple/..., <- operating System
-                              Failover-Only=true/false | <- failover-only setting
+                              Failover-Only=true/false, <- failover-only setting
+                              ScanTime=N, <- --scan-time setting
+                              Queue=N, <- --queue setting
+                              Expiry=N| <- --expiry setting
 
  summary       SUMMARY        The status summary of the miner
                               e.g. Elapsed=NNN,Found Blocks=N,Getworks=N,...|
@@ -259,6 +262,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
@@ -287,6 +306,30 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               Current Block Hash=XXXX..., <- blank if none
                               LP=true/false| <- LP is in use on at least 1 pool
 
+ debug|setting (*)
+               DEBUG          Debug settings
+                              The optional commands for 'setting' are the same as
+                              the screen curses debug settings
+                              You can only specify one setting
+                              Only the first character is checked (case insensitive):
+                              Silent, Quiet, Verbose, Debug, RPCProto, PerDevice,
+                              WorkTime, Normal
+                              The output fields are (as above):
+                              Silent=true/false,
+                              Quiet=true/false,
+                              Verbose=true/false,
+                              Debug=true/false,
+                              RPCProto=true/false,
+                              PerDevice=true/false,
+                              WorkTime=true/false|
+
+ setconfig|name,N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of setting 'name' to N
+                              The valid values for name are currently:
+                              queue, scantime, expiry
+                              N is an integer in the range 0 to 9999
+
 When you enable, disable or restart a GPU or PGA, you will also get Thread messages
 in the cgminer status window
 
@@ -306,8 +349,9 @@ windows
 Obviously, the JSON format is simply just the names as given before the '='
 with the values after the '='
 
-If you enable cgminer debug (-D or --debug) you will also get messages showing
-details of the requests received and the replies
+If you enable cgminer debug (-D or --debug) or, when cgminer debug is off,
+turn on debug with the API command 'debug|debug' you will also get messages
+showing some details of the requests received and the replies
 
 There are included 4 program examples for accessing the API:
 
@@ -339,13 +383,32 @@ miner.php - an example web page to access the API
 Feature Changelog for external applications using the API:
 
 
-API V1.18
+API V1.19
+
+Added API commands:
+ 'debug'
+ 'pgaidentify|N' (only works for BFL Singles so far)
+ 'setconfig|name,N'
 
 Modified API commands:
- 'stats' - add 'Work Had Roll Time', 'Work Can Roll', 'Work Had Expire',
-		'Work Roll Time' to the pool stats
+ 'devs' - add 'Diff1 Work', 'Difficulty Accepted', 'Difficulty Rejected',
+              'Last Share Difficulty' to all devices
+ 'gpu|N' - add 'Diff1 Work', 'Difficulty Accepted',
+              'Difficulty Rejected', 'Last Share Difficulty'
+ 'pga|N' - add 'Diff1 Work', 'Difficulty Accepted',
+              'Difficulty Rejected', 'Last Share Difficulty'
+ 'notify' - add '*Dev Throttle' (for BFL Singles)
+ 'pools' - add 'Proxy Type', 'Proxy', 'Difficulty Accepted', 'Difficulty Rejected',
+               'Difficulty Stale', 'Last Share Difficulty'
+ 'config' - add 'Queue', 'Expiry'
+
+----------
+
+API V1.18 (cgminer v2.7.4)
 
 Modified API commands:
+ 'stats' - add 'Work Had Roll Time', 'Work Can Roll', 'Work Had Expire',
+		'Work Roll Time' to the pool stats
  'config' - include 'ScanTime'
 
 ----------
@@ -866,6 +929,14 @@ e.g. $rigs = array('127.0.0.1:4028','myrig.com:4028:Sugoi');
 
 ---------
 
+Default:
+ $rigipsecurity = true;
+
+Set $rigipsecurity to false to show the IP/Port of the rig
+in the socket error messages and also show the full socket message
+
+---------
+
 Default:
  $rigtotals = true;
  $forcerigtotals = false;

+ 66 - 6
README

@@ -168,7 +168,7 @@ Options for both config file and command line:
 --scrypt            Use the scrypt algorithm for mining (litecoin only)
 --sharelog <arg>    Append share log to file
 --shares <arg>      Quit after mining N shares (default: unlimited)
---socks-proxy <arg> Set socks4 proxy (host:port)
+--socks-proxy <arg> Set socks4 proxy (host:port) for all pools without a proxy specified
 --syslog            Use system log for output messages (default: standard error)
 --temp-cutoff <arg> Temperature where a device will be automatically disabled, one value or comma separated list (default: 95)
 --text-only|-T      Disable ncurses formatted screen output
@@ -239,6 +239,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
 
@@ -295,6 +296,28 @@ Add overclocking settings, GPU and fan control with different engine settings fo
 
 cgminer -o http://pool:port -u username -p password -I 9 --auto-fan --auto-gpu --gpu-engine 750-950,945,700-930,960 --gpu-memclock 300
 
+Single pool with a standard http proxy, regular desktop:
+
+cgminer -o "http:proxy:port|http://pool:port" -u username -p password
+
+Single pool with a socks5 proxy, regular desktop:
+
+cgminer -o "socks5:proxy:port|http://pool:port" -u username -p password
+
+The list of proxy types are:
+ http:    standard http 1.1 proxy
+ http0:   http 1.0 proxy
+ socks4:  socks4 proxy
+ socks5:  socks5 proxy
+ socks4a: socks4a proxy
+ socks5h: socks5 proxy using a hostname
+
+If you compile cgminer with a version of CURL before 7.19.4 then some of the above will
+not be available. All are available since CURL version 7.19.4
+
+If you specify the --socks-proxy option to cgminer, it will only be applied to all pools
+that don't specify their own proxy setting like above
+
 READ WARNINGS AND DOCUMENTATION BELOW ABOUT OVERCLOCKING
 
 On Linux you virtually always need to export your display settings before
@@ -451,6 +474,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 cgminer with the --sharelog option, you can get detailed
 information for each share found. The argument to the option may be "-" for
@@ -816,7 +859,7 @@ driver version and ATI stream version.
 
 Q: cgminer reports no devices or only one device on startup on Linux although
 I have multiple devices and drivers+SDK installed properly?
-A: Try 'export DISPLAY=:0" before running cgminer.
+A: Try "export DISPLAY=:0" before running cgminer.
 
 Q: My network gets slower and slower and then dies for a minute?
 A; Try the --net-delay option.
@@ -843,10 +886,27 @@ They are Field-Programmable Gate Arrays that have been programmed to do Bitcoin
 mining. Since the acronym needs to be only 3 characters, the "Field-" part has
 been skipped.
 
-Q: How do I get my BFL device to auto-recognise?
-A: The only thing that needs to be done is to load the driver for them, which
-on linux would require:
-sudo modprobe ftdi_sio vendor=0x0403 product=0x6014
+Q: How do I get my BFL/Icarus/Lancelot/Cairnsmore device to auto-recognise?
+A: On linux, if the /dev/ttyUSB* devices don't automatically appear, the only
+thing that needs to be done is to load the driver for them:
+BFL: sudo modprobe ftdi_sio vendor=0x0403 product=0x6014
+Icarus: sudo modprobe pl2303 vendor=0x067b product=0x230
+Lancelot: sudo modprobe ftdi_sio vendor=0x0403 product=0x6001
+Cairnsmore: sudo modprobe ftdi_sio product=0x8350 vendor=0x0403
+On windows you must install the pl2303 or ftdi driver required for the device
+pl2303: http://prolificusa.com/pl-2303hx-drivers/
+ftdi: http://www.ftdichip.com/Drivers/VCP.htm
+
+Q: On linux I can see the /dev/ttyUSB* devices for my ICA/BFL/MMQ FPGA, but
+cgminer can't mine on them
+A: Make sure you have the required priviledges to access the /dev/ttyUSB* devices:
+ sudo ls -las /dev/ttyUSB*
+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 -G dialout `whoami`
+Then logout and back in again
 
 
 ---

+ 227 - 7
api.c

@@ -166,11 +166,13 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 
-static const char *APIVERSION = "1.18";
+static const char *APIVERSION = "1.19";
 static const char *DEAD = "Dead";
+#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
 static const char *SICK = "Sick";
 static const char *NOSTART = "NoStart";
 static const char *INIT = "Initialising";
+#endif
 static const char *DISABLED = "Disabled";
 static const char *ALIVE = "Alive";
 static const char *REJECTING = "Rejecting";
@@ -258,6 +260,8 @@ static const char *OSINFO =
 #define _MINESTATS	"STATS"
 #define _CHECK		"CHECK"
 #define _MINECOIN	"COIN"
+#define _DEBUGSET	"DEBUG"
+#define _SETCONFIG	"SETCONFIG"
 
 static const char ISJSON = '{';
 #define JSON0		"{"
@@ -295,6 +299,8 @@ static const char ISJSON = '{';
 #define JSON_MINESTATS	JSON1 _MINESTATS JSON2
 #define JSON_CHECK	JSON1 _CHECK JSON2
 #define JSON_MINECOIN	JSON1 _MINECOIN JSON2
+#define JSON_DEBUGSET	JSON1 _DEBUGSET JSON2
+#define JSON_SETCONFIG	JSON1 _SETCONFIG JSON2
 #define JSON_END	JSON4 JSON5
 
 static const char *JSON_COMMAND = "command";
@@ -390,6 +396,14 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_INVBOOL 76
 #define MSG_FOO 77
 #define MSG_MINECOIN 78
+#define MSG_DEBUGSET 79
+#define MSG_PGAIDENT 80
+#define MSG_PGANOID 81
+#define MSG_SETCONFIG 82
+#define MSG_UNKCON 83
+#define MSG_INVNUM 84
+#define MSG_CONPAR 85
+#define MSG_CONVAL 86
 
 enum code_severity {
 	SEVERITY_ERR,
@@ -418,6 +432,7 @@ enum code_parameters {
 	PARAM_STR,
 	PARAM_BOTH,
 	PARAM_BOOL,
+	PARAM_SET,
 	PARAM_NONE
 };
 
@@ -543,12 +558,24 @@ struct CODES {
  { SEVERITY_ERR,   MSG_INVBOOL,	PARAM_NONE,	"Invalid parameter should be true or false" },
  { SEVERITY_SUCC,  MSG_FOO,	PARAM_BOOL,	"Failover-Only set to %s" },
  { SEVERITY_SUCC,  MSG_MINECOIN,PARAM_NONE,	"CGMiner coin" },
+ { SEVERITY_SUCC,  MSG_DEBUGSET,PARAM_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" },
+ { SEVERITY_ERR,   MSG_CONPAR,	PARAM_NONE,	"Missing config parameters 'name,N'" },
+ { SEVERITY_ERR,   MSG_CONVAL,	PARAM_STR,	"Missing config value N for '%s,N'" },
  { SEVERITY_FAIL, 0, 0, NULL }
 };
 
 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;
@@ -749,6 +776,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;
@@ -875,6 +903,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);
+}
+
 static struct api_data *print_data(struct api_data *root, char *buf, bool isjson)
 {
 	struct api_data *tmp;
@@ -946,6 +979,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;
@@ -1155,6 +1191,9 @@ static char *message(int messageid, int paramid, char *param2, bool isjson)
 				case PARAM_BOOL:
 					sprintf(buf, codes[i].description, paramid ? TRUESTR : FALSESTR);
 					break;
+				case PARAM_SET:
+					sprintf(buf, codes[i].description, param2, paramid);
+					break;
 				case PARAM_NONE:
 				default:
 					strcpy(buf, codes[i].description);
@@ -1163,8 +1202,8 @@ static char *message(int messageid, int paramid, char *param2, bool isjson)
 			root = api_add_string(root, _STATUS, severity, false);
 			root = api_add_time(root, "When", &when, false);
 			root = api_add_int(root, "Code", &messageid, false);
-			root = api_add_string(root, "Msg", buf, false);
-			root = api_add_string(root, "Description", opt_api_description, false);
+			root = api_add_escape(root, "Msg", buf, false);
+			root = api_add_escape(root, "Description", opt_api_description, false);
 
 			root = print_data(root, ptr, isjson);
 			if (isjson)
@@ -1178,8 +1217,8 @@ static char *message(int messageid, int paramid, char *param2, bool isjson)
 	int id = -1;
 	root = api_add_int(root, "Code", &id, false);
 	sprintf(buf, "%d", messageid);
-	root = api_add_string(root, "Msg", buf, false);
-	root = api_add_string(root, "Description", opt_api_description, false);
+	root = api_add_escape(root, "Msg", buf, false);
+	root = api_add_escape(root, "Description", opt_api_description, false);
 
 	root = print_data(root, ptr, isjson);
 	if (isjson)
@@ -1257,6 +1296,8 @@ static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
 	root = api_add_const(root, "OS", OSINFO, false);
 	root = api_add_bool(root, "Failover-Only", &opt_fail_only, false);
 	root = api_add_int(root, "ScanTime", &opt_scantime, false);
+	root = api_add_int(root, "Queue", &opt_queue, false);
+	root = api_add_int(root, "Expiry", &opt_expiry, false);
 
 	root = print_data(root, buf, isjson);
 	if (isjson)
@@ -1264,6 +1305,7 @@ static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
 	strcat(io_buffer, buf);
 }
 
+#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
 static const char *status2str(enum alive status)
 {
 	switch (status) {
@@ -1281,6 +1323,7 @@ static const char *status2str(enum alive status)
 			return UNKNOWN;
 	}
 }
+#endif
 
 #ifdef HAVE_OPENCL
 static void gpustatus(int gpu, bool isjson)
@@ -1341,6 +1384,10 @@ static void gpustatus(int gpu, bool isjson)
 		root = api_add_int(root, "Last Share Pool", &last_share_pool, false);
 		root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false);
 		root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false);
+		root = api_add_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);
 
 		root = print_data(root, buf, isjson);
 		strcat(io_buffer, buf);
@@ -1424,6 +1471,10 @@ static void pgastatus(int pga, 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_freq(root, "Frequency", &frequency, 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);
 
 		root = print_data(root, buf, isjson);
 		strcat(io_buffer, buf);
@@ -1456,6 +1507,10 @@ static void cpustatus(int cpu, bool isjson)
 		root = api_add_int(root, "Last Share Pool", &last_share_pool, false);
 		root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false);
 		root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false);
+		root = api_add_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);
 
 		root = print_data(root, buf, isjson);
 		strcat(io_buffer, buf);
@@ -1693,6 +1748,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
@@ -1790,6 +1883,17 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
 		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 Shares", &(pool->diff1), false);
+		if (pool->rpc_proxy) {
+			root = api_add_const(root, "Proxy Type", proxytype(pool->rpc_proxytype), false);
+			root = api_add_escape(root, "Proxy", pool->rpc_proxy, false);
+		} else {
+			root = api_add_const(root, "Proxy Type", BLANK, false);
+			root = api_add_const(root, "Proxy", BLANK, false);
+		}
+		root = api_add_diff(root, "Difficulty Accepted", &(pool->diff_accepted), false);
+		root = api_add_diff(root, "Difficulty Rejected", &(pool->diff_rejected), false);
+		root = api_add_diff(root, "Difficulty Stale", &(pool->diff_stale), false);
+		root = api_add_diff(root, "Last Share Difficulty", &(pool->last_share_diff), false);
 
 		if (isjson && (i > 0))
 			strcat(io_buffer, COMMA);
@@ -1842,6 +1946,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)
@@ -2554,6 +2661,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);
@@ -2772,7 +2880,7 @@ static void minecoin(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo
 #endif
 		root = api_add_const(root, "Hash Method", SHA256STR, false);
 
-        mutex_lock(&ch_lock);
+	mutex_lock(&ch_lock);
 	if (current_fullhash && *current_fullhash) {
 		root = api_add_timeval(root, "Current Block Time", &block_timeval, true);
 		root = api_add_string(root, "Current Block Hash", current_fullhash, true);
@@ -2781,7 +2889,7 @@ static void minecoin(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo
 		root = api_add_timeval(root, "Current Block Time", &t, true);
 		root = api_add_const(root, "Current Block Hash", BLANK, false);
 	}
-        mutex_unlock(&ch_lock);
+	mutex_unlock(&ch_lock);
 
 	root = api_add_bool(root, "LP", &have_longpoll, false);
 
@@ -2791,6 +2899,115 @@ static void minecoin(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo
 	strcat(io_buffer, buf);
 }
 
+static void debugstate(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	struct api_data *root = NULL;
+	char buf[TMPBUFSIZ];
+
+	if (param == NULL)
+		param = (char *)BLANK;
+	else
+		*param = tolower(*param);
+
+	switch(*param) {
+	case 's':
+		opt_realquiet = true;
+		break;
+	case 'q':
+		opt_quiet ^= true;
+		break;
+	case 'v':
+		opt_log_output ^= true;
+		if (opt_log_output)
+			opt_quiet = false;
+		break;
+	case 'd':
+		opt_debug ^= true;
+		opt_log_output = opt_debug;
+		if (opt_debug)
+			opt_quiet = false;
+		break;
+	case 'r':
+		opt_protocol ^= true;
+		if (opt_protocol)
+			opt_quiet = false;
+		break;
+	case 'p':
+		want_per_device_stats ^= true;
+		opt_log_output = want_per_device_stats;
+		break;
+	case 'n':
+		opt_log_output = false;
+		opt_debug = false;
+		opt_quiet = false;
+		opt_protocol = false;
+		want_per_device_stats = false;
+		opt_worktime = false;
+		break;
+	case 'w':
+		opt_worktime ^= true;
+		break;
+	default:
+		// anything else just reports the settings
+		break;
+	}
+
+	sprintf(io_buffer, isjson
+		? "%s," JSON_DEBUGSET
+		: "%s" _DEBUGSET ",",
+		message(MSG_DEBUGSET, 0, NULL, isjson));
+
+	root = api_add_bool(root, "Silent", &opt_realquiet, false);
+	root = api_add_bool(root, "Quiet", &opt_quiet, false);
+	root = api_add_bool(root, "Verbose", &opt_log_output, false);
+	root = api_add_bool(root, "Debug", &opt_debug, false);
+	root = api_add_bool(root, "RPCProto", &opt_protocol, false);
+	root = api_add_bool(root, "PerDevice", &want_per_device_stats, false);
+	root = api_add_bool(root, "WorkTime", &opt_worktime, false);
+
+	root = print_data(root, buf, isjson);
+	if (isjson)
+		strcat(buf, JSON_CLOSE);
+	strcat(io_buffer, buf);
+}
+
+static void setconfig(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
+{
+	char *comma;
+	int value;
+
+	if (param == NULL || *param == '\0') {
+		strcpy(io_buffer, message(MSG_CONPAR, 0, NULL, isjson));
+		return;
+	}
+
+	comma = strchr(param, ',');
+	if (!comma) {
+		strcpy(io_buffer, message(MSG_CONVAL, 0, param, isjson));
+		return;
+	}
+
+	*(comma++) = '\0';
+	value = atoi(comma);
+	if (value < 0 || value > 9999) {
+		strcpy(io_buffer, message(MSG_INVNUM, value, param, isjson));
+		return;
+	}
+
+	if (strcasecmp(param, "queue") == 0)
+		opt_queue = value;
+	else if (strcasecmp(param, "scantime") == 0)
+		opt_scantime = value;
+	else if (strcasecmp(param, "expiry") == 0)
+		opt_expiry = value;
+	else {
+		strcpy(io_buffer, message(MSG_UNKCON, 0, param, isjson));
+		return;
+	}
+
+	strcpy(io_buffer, message(MSG_SETCONFIG, value, param, isjson));
+}
+
 static void checkcommand(__maybe_unused SOCKETTYPE c, char *param, bool isjson, char group);
 
 struct CMDS {
@@ -2813,6 +3030,7 @@ struct CMDS {
 	{ "pga",		pgadev,		false },
 	{ "pgaenable",		pgaenable,	true },
 	{ "pgadisable",		pgadisable,	true },
+	{ "pgaidentify",	pgaidentify,	true },
 #endif
 #ifdef WANT_CPUMINE
 	{ "cpu",		cpudev,		false },
@@ -2843,6 +3061,8 @@ struct CMDS {
 	{ "check",		checkcommand,	false },
 	{ "failover-only",	failoveronly,	true },
 	{ "coin",		minecoin,	false },
+	{ "debug",		debugstate,	true },
+	{ "setconfig",		setconfig,	true },
 	{ NULL,			NULL,		false }
 };
 

+ 190 - 40
cgminer.c

@@ -86,14 +86,14 @@ static char packagename[255];
 bool opt_protocol;
 static bool opt_benchmark;
 bool have_longpoll;
-static bool want_per_device_stats;
+bool want_per_device_stats;
 bool use_syslog;
 bool opt_quiet;
-static bool opt_realquiet;
+bool opt_realquiet;
 bool opt_loginput;
 const int opt_cutofftemp = 95;
 int opt_log_interval = 5;
-static int opt_queue = 1;
+int opt_queue = 1;
 int opt_scantime = 60;
 int opt_expiry = 120;
 int opt_bench_algo = -1;
@@ -142,6 +142,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;
@@ -186,6 +187,7 @@ pthread_mutex_t control_lock;
 int hw_errors;
 int total_accepted, total_rejected, total_diff1;
 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;
 static unsigned int work_block;
@@ -226,6 +228,7 @@ static struct block *blocks = NULL;
 char *opt_socks_proxy = NULL;
 
 static const char def_conf[] = "cgminer.conf";
+static char *default_config;
 static bool config_loaded;
 static int include_count;
 #define JSON_INCLUDE_CONF "include"
@@ -357,7 +360,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");
@@ -419,6 +422,8 @@ static struct pool *add_pool(void)
 	/* Make sure the pool doesn't think we've been idle since time 0 */
 	pool->tv_idle.tv_sec = ~0UL;
 
+	pool->rpc_proxy = NULL;
+
 	return pool;
 }
 
@@ -547,6 +552,8 @@ static char *set_url(char *arg)
 		add_pool();
 	pool = pools[total_urls - 1];
 
+	arg = get_proxy(arg, pool);
+
 	opt_set_charp(arg, &pool->rpc_url);
 	if (strncmp(arg, "http://", 7) &&
 	    strncmp(arg, "https://", 8)) {
@@ -1058,6 +1065,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
@@ -1171,21 +1181,21 @@ static char *load_config(const char *arg, void __maybe_unused *unused)
 	return parse_config(config, true);
 }
 
+static char *set_default_config(const char *arg)
+{
+	opt_set_charp(arg, &default_config);
+
+	return NULL;
+}
+
+void default_save_file(char *filename);
+
 static void load_default_config(void)
 {
 	cnfbuf = malloc(PATH_MAX);
 
-#if defined(unix)
-	if (getenv("HOME") && *getenv("HOME")) {
-	        strcpy(cnfbuf, getenv("HOME"));
-		strcat(cnfbuf, "/");
-	} else
-		strcpy(cnfbuf, "");
-	strcat(cnfbuf, ".cgminer/");
-#else
-	strcpy(cnfbuf, "");
-#endif
-	strcat(cnfbuf, def_conf);
+	default_save_file(cnfbuf);
+
 	if (!access(cnfbuf, R_OK))
 		load_config(cnfbuf, NULL);
 	else {
@@ -1233,6 +1243,10 @@ static struct opt_table opt_cmdline_table[] = {
 		     load_config, NULL, NULL,
 		     "Load a JSON-format configuration file\n"
 		     "See example.conf for an example configuration."),
+	OPT_WITH_ARG("--default-config",
+		     set_default_config, NULL, NULL,
+		     "Specify the filename of the default config file\n"
+		     "Loaded at start and used when saving without a name."),
 	OPT_WITHOUT_ARG("--help|-h",
 			opt_verusage_and_exit, NULL,
 			"Print this message"),
@@ -1755,7 +1769,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] = "";
 
 #ifdef __BIG_ENDIAN__
         int swapcounter = 0;
@@ -1780,8 +1796,10 @@ 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);
+	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)) {
@@ -1803,6 +1821,47 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub
 			sprintf(hashshow, "%08lx.%08lx%s", (unsigned long)(hash32[6]), (unsigned long)(hash32[5]),
 				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
@@ -1812,18 +1871,23 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub
 		cgpu->accepted++;
 		total_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;
 		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) {
@@ -1845,6 +1909,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) {
@@ -1872,8 +1939,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);
 		}
 
@@ -1973,6 +2040,25 @@ 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)
+{
+	double targ;
+	int i;
+
+	targ = 0;
+	for (i = 31; i >= 0; i--) {
+		targ *= 256;
+		targ += work->target[i];
+	}
+
+	work->work_difficulty = DIFFEXACTONE / (targ ? : DIFFEXACTONE);
+}
+
 static void get_benchmark_work(struct work *work)
 {
 	// Use a random work block pulled from a pool
@@ -1985,13 +2071,17 @@ 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 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;
@@ -2000,7 +2090,7 @@ static bool get_upstream_work(struct work *work, CURL *curl)
 
 	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);
@@ -2013,8 +2103,8 @@ static bool get_upstream_work(struct work *work, CURL *curl)
 	} 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;
 
@@ -2031,6 +2121,8 @@ static bool get_upstream_work(struct work *work, CURL *curl)
 
 	work->pool = pool;
 	work->longpoll = false;
+	work->getwork_mode = GETWORK_MODE_POOL;
+	calc_diff(work);
 	total_getworks++;
 	pool->getwork_requested++;
 
@@ -2339,6 +2431,7 @@ static struct work *make_clone(struct work *work)
 
 	memcpy(work_clone, work, sizeof(struct 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
@@ -2537,6 +2630,8 @@ static void *submit_work_thread(void *userdata)
 			sharelog("discard", work);
 			total_stale++;
 			pool->stale_shares++;
+			total_diff_stale += work->work_difficulty;
+			pool->diff_stale += work->work_difficulty;
 			goto out;
 		}
 		work->stale = true;
@@ -2550,6 +2645,8 @@ static void *submit_work_thread(void *userdata)
 			applog(LOG_NOTICE, "Share became stale while retrying submit, discarding");
 			total_stale++;
 			pool->stale_shares++;
+			total_diff_stale += work->work_difficulty;
+			pool->diff_stale += work->work_difficulty;
 			break;
 		}
 
@@ -2996,6 +3093,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;
@@ -3089,7 +3188,11 @@ void write_config(FILE *fcfg)
 	/* Write pool values */
 	fputs("{\n\"pools\" : [", fcfg);
 	for(i = 0; i < total_pools; i++) {
-		fprintf(fcfg, "%s\n\t{\n\t\t\"url\" : \"%s\",", i > 0 ? "," : "", json_escape(pools[i]->rpc_url));
+		fprintf(fcfg, "%s\n\t{\n\t\t\"url\" : \"%s%s%s%s\",", i > 0 ? "," : "",
+			pools[i]->rpc_proxy ? json_escape((char *)proxytype(pools[i]->rpc_proxytype)) : "",
+			pools[i]->rpc_proxy ? json_escape(pools[i]->rpc_proxy) : "",
+			pools[i]->rpc_proxy ? "|" : "",
+			json_escape(pools[i]->rpc_url));
 		fprintf(fcfg, "\n\t\t\"user\" : \"%s\",", json_escape(pools[i]->rpc_user));
 		fprintf(fcfg, "\n\t\t\"pass\" : \"%s\"\n\t}", json_escape(pools[i]->rpc_pass));
 		}
@@ -3412,12 +3515,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 ? "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();
@@ -3470,6 +3574,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();
 
@@ -3480,6 +3588,11 @@ retry:
 
 void default_save_file(char *filename)
 {
+	if (default_config && *default_config) {
+		strcpy(filename, default_config);
+		return;
+	}
+
 #if defined(unix)
 	if (getenv("HOME") && *getenv("HOME")) {
 	        strcpy(filename, getenv("HOME"));
@@ -3794,6 +3907,7 @@ static void *longpoll_thread(void *userdata);
 
 static bool pool_active(struct pool *pool, bool pinging)
 {
+	struct timeval tv_getwork, tv_getwork_reply;
 	bool ret = false;
 	json_t *val;
 	CURL *curl;
@@ -3806,8 +3920,10 @@ static bool pool_active(struct pool *pool, bool pinging)
 	}
 
 	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) {
 		struct work *work = make_work();
@@ -3819,6 +3935,10 @@ static bool pool_active(struct pool *pool, bool pinging)
 			       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);
@@ -4110,7 +4230,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;
 
@@ -4125,7 +4245,8 @@ bool submit_work_sync(struct thr_info *thr, const struct work *work_in)
 	wc->cmd = WC_SUBMIT_WORK;
 	wc->thr = thr;
 	memcpy(wc->work, work_in, sizeof(*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");
 
@@ -4141,7 +4262,7 @@ err_out:
 	return false;
 }
 
-bool hashtest(const struct work *work)
+static bool hashtest(struct thr_info *thr, const struct work *work)
 {
 	uint32_t *data32 = (uint32_t *)(work->data);
 	unsigned char swap[128];
@@ -4162,11 +4283,22 @@ bool hashtest(const struct work *work)
 
 	memcpy((void*)work->hash, hash2, 32);
 
-	return fulltest(work->hash, work->target);
+	if (hash2_32[7] != 0) {
+		applog(LOG_WARNING, "%s%d: invalid nonce - HW error",
+				thr->cgpu->api->name, thr->cgpu->device_id);
+		hw_errors++;
+		thr->cgpu->hw_errors++;
+		return false;
+	}
+
+	bool test = fulltest(work->hash, work->target);
+	if (!test)
+		applog(LOG_INFO, "Share below target");
 
+	return test;
 }
 
-bool test_nonce(struct work *work, uint32_t nonce)
+static bool test_nonce(struct thr_info *thr, struct work *work, uint32_t nonce)
 {
 	if (opt_scrypt) {
 		uint32_t *work_nonce = (uint32_t *)(work->data + 64 + 12);
@@ -4180,21 +4312,24 @@ bool test_nonce(struct work *work, uint32_t nonce)
 	work->data[64 + 12 + 2] = (nonce >> 16) & 0xff;
 	work->data[64 + 12 + 3] = (nonce >> 24) & 0xff;
 
-	return hashtest(work);
+	return hashtest(thr, work);
 }
 
 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++;
 
 	/* Do one last check before attempting to submit the work */
 	/* Side effect: sets work->data for us */
-	if (!test_nonce(work, nonce)) {
-		applog(LOG_INFO, "Share below target");
+	if (!test_nonce(thr, work, nonce))
 		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)
@@ -4309,6 +4444,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);
@@ -4405,7 +4542,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)
+static void convert_to_work(json_t *val, int rolltime, struct pool *pool, struct timeval *tv_lp, struct timeval *tv_lp_reply)
 {
 	struct work *work;
 	bool rc;
@@ -4421,6 +4558,10 @@ static void convert_to_work(json_t *val, int rolltime, struct pool *pool)
 	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;
@@ -4489,7 +4630,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;
@@ -4534,13 +4675,16 @@ retry_pool:
 		curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1);
 		val = json_rpc_call(curl, pool->lp_url, pool->rpc_userpass, rpc_req,
 				    false, true, &rolltime, pool, false);
+
+		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);
+			convert_to_work(val, rolltime, pool, &start, &reply);
 			failures = 0;
 			json_decref(val);
 		} else {
@@ -4867,6 +5011,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);
@@ -4892,6 +5038,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;
@@ -4988,6 +5136,8 @@ void add_pool_details(bool live, char *url, char *user, char *pass)
 
 	pool = add_pool();
 
+	url = get_proxy(url, pool);
+
 	pool->rpc_url = url;
 	pool->rpc_user = user;
 	pool->rpc_pass = pass;

+ 58 - 6
driver-bitforce.c

@@ -45,8 +45,8 @@ enum {
 #endif /* WIN32 */
 
 #include "compat.h"
-#include "fpgautils.h"
 #include "miner.h"
+#include "fpgautils.h"
 
 #define BITFORCE_SLEEP_MS 500
 #define BITFORCE_TIMEOUT_S 7
@@ -99,6 +99,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_ERR, "BFL: Error reading/timeout (ZGX)");
@@ -229,7 +230,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)
@@ -309,6 +310,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])) {
@@ -338,6 +340,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;
@@ -348,16 +381,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);
 	
@@ -385,11 +425,14 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 		/* Use the temperature monitor as a kind of watchdog for when
 		 * our responses are out of sync and flush the buffer to
 		 * hopefully recover */
-		applog(LOG_WARNING, "BFL%i: Garbled response probably throttling, clearing buffer");
+		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;
@@ -411,6 +454,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);
@@ -450,6 +494,7 @@ re_send:
 		BFwrite(fdDev, ob, 68);
 	}
 
+	pdevbuf[0] = '\0';
 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	mutex_unlock(&bitforce->device_mutex);
 
@@ -493,6 +538,7 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 
 		mutex_lock(&bitforce->device_mutex);
 		BFwrite(fdDev, "ZFX", 3);
+		pdevbuf[0] = '\0';
 		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 		mutex_unlock(&bitforce->device_mutex);
 
@@ -637,6 +683,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;
@@ -673,6 +724,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,

+ 2 - 2
driver-cpu.c

@@ -75,7 +75,7 @@ static inline void affine_to_cpu(int id, 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);
 
@@ -827,7 +827,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

@@ -49,8 +49,8 @@
 #endif
 
 #include "elist.h"
-#include "fpgautils.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
@@ -598,7 +598,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);
 }
 
 static bool icarus_prepare(struct thr_info *thr)
@@ -639,7 +639,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	struct timeval tv_start, tv_finish, 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;
@@ -712,7 +713,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	nonce = swab32(nonce);
 #endif
 
+	curr_hw_errors = icarus->hw_errors;
 	submit_nonce(thr, work, nonce);
+	was_hw_error = (curr_hw_errors > icarus->hw_errors);
 
 	hash_count = (nonce & info->nonce_mask);
 	hash_count++;
@@ -726,8 +729,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 				icarus->device_id, nonce, hash_count, elapsed.tv_sec, elapsed.tv_usec);
 	}
 
-	// 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);

+ 7 - 9
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"
@@ -103,7 +103,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__);
@@ -404,7 +404,7 @@ modminer_process_results(struct thr_info*thr)
 	char cmd[2], temperature;
 	uint32_t nonce;
 	long iter;
-	bool bad;
+	int curr_hw_errors;
 	cmd[0] = '\x0a';
 	cmd[1] = fpgaid;
 
@@ -441,12 +441,10 @@ modminer_process_results(struct thr_info*thr)
 		mutex_unlock(&modminer->device_mutex);
 		if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) {
 			state->no_nonce_counter = 0;
-			bad = !test_nonce(work, nonce);
-			if (!bad)
-				submit_nonce(thr, work, nonce);
-			else {
-				++hw_errors;
-				if (++modminer->hw_errors * 100 > 1000 + state->good_share_counter)
+			curr_hw_errors = modminer->hw_errors;
+			submit_nonce(thr, work, nonce);
+			if (modminer->hw_errors > curr_hw_errors) {
+				if (modminer->hw_errors * 100 > 1000 + state->good_share_counter)
 					// Only reduce clocks if hardware errors are more than ~1% of results
 					modminer_reduce_clock(thr, true);
 			}

+ 14 - 2
findnonce.c

@@ -131,6 +131,8 @@ void precalc_hash(dev_blk_ctx *blk, uint32_t *state, uint32_t *data)
 	blk->sevenA = blk->ctx_h + SHA256_K[7];
 }
 
+#if 0 // not used any more
+
 #define P(t) (W[(t)&0xF] = W[(t-16)&0xF] + (rotate(W[(t-15)&0xF], 25) ^ rotate(W[(t-15)&0xF], 14) ^ (W[(t-15)&0xF] >> 3)) + W[(t-7)&0xF] + (rotate(W[(t-2)&0xF], 15) ^ rotate(W[(t-2)&0xF], 13) ^ (W[(t-2)&0xF] >> 10)))
 
 #define IR(u) \
@@ -167,6 +169,8 @@ void precalc_hash(dev_blk_ctx *blk, uint32_t *state, uint32_t *data)
   R(E, F, G, H, A, B, C, D, P(u+4), SHA256_K[u+4]); \
   R(D, E, F, G, H, A, B, C, P(u+5), SHA256_K[u+5])
 
+#endif
+
 struct pc_data {
 	struct thr_info *thr;
 	struct work *work;
@@ -175,6 +179,8 @@ struct pc_data {
 	int found;
 };
 
+#if 0 // not used any more
+
 static void send_sha_nonce(struct pc_data *pcd, cl_uint nonce)
 {
 	dev_blk_ctx *blk = &pcd->work->blk;
@@ -222,6 +228,8 @@ static void send_sha_nonce(struct pc_data *pcd, cl_uint nonce)
 	}
 }
 
+#endif
+
 static void send_scrypt_nonce(struct pc_data *pcd, uint32_t nonce)
 {
 	struct thr_info *thr = pcd->thr;
@@ -238,6 +246,8 @@ static void send_scrypt_nonce(struct pc_data *pcd, uint32_t nonce)
 static void *postcalc_hash(void *userdata)
 {
 	struct pc_data *pcd = (struct pc_data *)userdata;
+	struct thr_info *thr = pcd->thr;
+	struct work *work = pcd->work;
 	unsigned int entry = 0;
 
 	pthread_detach(pthread_self());
@@ -248,8 +258,10 @@ static void *postcalc_hash(void *userdata)
 		applog(LOG_DEBUG, "OCL NONCE %u found in slot %d", nonce, entry);
 		if (opt_scrypt)
 			send_scrypt_nonce(pcd, nonce);
-		else
-			send_sha_nonce(pcd, nonce);
+		else {
+			if (unlikely(submit_nonce(thr, work, nonce) == false))
+				applog(LOG_ERR, "Failed to submit work, exiting");
+		}
 	}
 
 	free(pcd);

+ 24 - 29
fpgautils.c

@@ -33,13 +33,12 @@
 #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);
@@ -69,15 +68,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;
@@ -107,30 +104,32 @@ serial_autodetect_devserial(detectone_func_t detectone, const char*prodname)
 #endif
 }
 
-int
-_serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto)
+int _serial_detect(struct device_api *api, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto)
 {
 	struct string_elist *iter, *tmp;
-	const char*s, *p;
+	const char *dev, *colon;
 	bool inhibitauto = false;
 	char found = 0;
-	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;
 		}
-		if (!strcmp(s, "auto"))
+		if (!strcmp(dev, "auto"))
 			forceauto = true;
-		else
-		if (!strcmp(s, "noauto"))
+		else if (!strcmp(dev, "noauto"))
 			inhibitauto = true;
-		else
-		if (detectone(s)) {
+		else if (detectone(dev)) {
 			string_elist_del(iter);
 			inhibitauto = true;
 			++found;
@@ -311,8 +310,7 @@ void termios_debug(const char *devpath, struct termios *my_termios, const char *
 #endif
 #endif
 
-int
-serial_open(const char*devpath, unsigned long baud, signed short timeout, bool purge)
+int serial_open(const char *devpath, unsigned long baud, signed short timeout, bool purge)
 {
 #ifdef WIN32
 	HANDLE hSerial = CreateFile(devpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
@@ -429,8 +427,7 @@ serial_open(const char*devpath, unsigned long baud, signed short timeout, bool p
 #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) {
@@ -446,8 +443,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);
@@ -471,8 +467,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;
 

+ 13 - 13
fpgautils.h

@@ -16,24 +16,24 @@
 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, bool force_autoscan);
-#define serial_detect_fauto(dname, detectone, autoscan)  \
-	_serial_detect(dname, detectone, autoscan, true)
-#define serial_detect_auto(dname, detectone, autoscan)  \
-	_serial_detect(dname, detectone, autoscan, false)
-#define serial_detect(dname, detectone)  \
-	_serial_detect(dname, detectone,     NULL, false)
-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_detect(struct device_api *api, detectone_func_t, autoscan_func_t, bool force_autoscan);
+#define serial_detect_fauto(api, detectone, autoscan)  \
+	_serial_detect(api, detectone, autoscan, true)
+#define serial_detect_auto(api, detectone, autoscan)  \
+	_serial_detect(api, detectone, autoscan, false)
+#define serial_detect(api, detectone)  \
+	_serial_detect(api, detectone, NULL, false)
+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_open(const char*devpath, unsigned long baud, signed short 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, signed short 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

+ 2 - 0
logging.h

@@ -20,6 +20,8 @@ enum {
 /* original / legacy debug flags */
 extern bool opt_debug;
 extern bool opt_log_output;
+extern bool opt_realquiet;
+extern bool want_per_device_stats;
 
 /* global log_level, messages with lower or equal prio are logged */
 extern int opt_log_level;

+ 40 - 4
miner.h

@@ -239,6 +239,7 @@ struct device_api {
 	void (*get_statline)(char*, 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*);
@@ -276,6 +277,7 @@ enum dev_reason {
 	REASON_DEV_OVER_HEAT,
 	REASON_DEV_THERMAL_CUTOFF,
 	REASON_DEV_COMMS_ERROR,
+	REASON_DEV_THROTTLE,
 };
 
 #define REASON_NONE			"None"
@@ -288,6 +290,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
@@ -335,6 +338,7 @@ struct cgpu_info {
 	uint32_t nonces;
 	bool nonce_range;
 	bool polling;
+	bool flash_led;
 #endif
 	pthread_mutex_t		device_mutex;
 
@@ -395,8 +399,12 @@ struct cgpu_info {
 	int gpu_powertune;
 	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;
@@ -410,6 +418,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;
 };
@@ -449,6 +458,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 double us_tdiff(struct timeval *end, struct timeval *start);
+extern double tdiff(struct timeval *end, struct timeval *start);
 
 struct string_elist {
 	char *string;
@@ -575,6 +585,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
@@ -585,6 +596,8 @@ extern const uint32_t sha256_init_state[];
 extern json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass,
 			     const char *rpc_req, bool, bool, int *,
 			     struct pool *pool, bool);
+extern const char *proxytype(curl_proxytype proxytype);
+extern char *get_proxy(char *url, struct pool *pool);
 extern char *bin2hex(const unsigned char *p, size_t len);
 extern bool hex2bin(unsigned char *p, const char *hexstr, size_t len);
 
@@ -598,7 +611,9 @@ typedef bool (*sha256_func)(struct thr_info*, const unsigned char *pmidstate,
 
 extern bool fulltest(const unsigned char *hash, const unsigned char *target);
 
+extern int opt_queue;
 extern int opt_scantime;
+extern int opt_expiry;
 
 extern pthread_mutex_t console_lock;
 extern pthread_mutex_t ch_lock;
@@ -671,6 +686,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;
@@ -735,6 +751,10 @@ struct pool {
 	int solved;
 	int diff1;
 
+	double diff_accepted;
+	double diff_rejected;
+	double diff_stale;
+
 	int queued;
 	int staged;
 
@@ -763,6 +783,8 @@ struct pool {
 	char *rpc_url;
 	char *rpc_userpass;
 	char *rpc_user, *rpc_pass;
+	curl_proxytype rpc_proxytype;
+	char *rpc_proxy;
 
 	pthread_mutex_t pool_lock;
 
@@ -778,11 +800,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];
@@ -813,9 +841,16 @@ struct work {
 
 	unsigned int	work_block;
 	int		id;
-	UT_hash_handle hh;
+	UT_hash_handle	hh;
+
+	double		work_difficulty;
 
-	time_t share_found_time;
+	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;
 };
 
 #ifdef USE_MODMINER 
@@ -837,7 +872,6 @@ struct modminer_fpga_state {
 #endif
 
 extern void get_datestamp(char *, struct timeval *);
-extern bool test_nonce(struct work *work, uint32_t nonce);
 bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce);
 extern void tailsprintf(char *f, const char *fmt, ...);
 extern void wlogprint(const char *f, ...);
@@ -880,7 +914,8 @@ enum api_data_type {
 	API_UTILITY,
 	API_FREQ,
 	API_VOLTS,
-	API_HS
+	API_HS,
+	API_DIFF
 };
 
 struct api_data {
@@ -911,5 +946,6 @@ 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);
 
 #endif /* __MINER_H__ */

+ 69 - 22
miner.php

@@ -2,7 +2,7 @@
 session_start();
 #
 global $title, $miner, $port, $readonly, $notify, $rigs;
-global $rigtotals, $forcerigtotals;
+global $rigipsecurity, $rigtotals, $forcerigtotals;
 global $socksndtimeoutsec, $sockrcvtimeoutsec;
 global $checklastshare, $poolinputs, $hidefields;
 global $ignorerefresh, $changerefresh, $autorefresh;
@@ -40,6 +40,10 @@ $poolinputs = false;
 #  format: 'IP:Port' or 'Host:Port' or 'Host:Port:Name'
 $rigs = array('127.0.0.1:4028');
 #
+# Set $rigipsecurity to false to show the IP/Port of the rig
+# in the socket error messages and also show the full socket message
+$rigipsecurity = true;
+#
 # Set $rigtotals to true to display totals on the single rig page
 # 'false' means no totals (and ignores $forcerigtotals)
 # You can force it to always show rig totals when there is only
@@ -226,7 +230,7 @@ function htmlhead($checkapi, $rig, $pg = null)
  if ($readonly === false && $checkapi === true)
  {
 	$error = null;
-	$access = api('privileged');
+	$access = api($rig, 'privileged');
 	if ($error != null
 	||  !isset($access['STATUS']['STATUS'])
 	||  $access['STATUS']['STATUS'] != 'S')
@@ -275,8 +279,9 @@ global $haderror, $error;
 $haderror = false;
 $error = null;
 #
-function getsock($addr, $port)
+function getsock($rig, $addr, $port)
 {
+ global $rigipsecurity;
  global $haderror, $error, $socksndtimeoutsec, $sockrcvtimeoutsec;
 
  $error = null;
@@ -285,9 +290,15 @@ function getsock($addr, $port)
  if ($socket === false || $socket === null)
  {
 	$haderror = true;
-	$error = socket_strerror(socket_last_error());
-	$msg = "socket create(TCP) failed";
-	$error = "ERR: $msg '$error'\n";
+	if ($rigipsecurity === false)
+	{
+		$error = socket_strerror(socket_last_error());
+		$msg = "socket create(TCP) failed";
+		$error = "ERR: $msg '$error'\n";
+	}
+	else
+		$error = "ERR: socket create(TCP) failed\n";
+
 	return null;
  }
 
@@ -301,9 +312,15 @@ function getsock($addr, $port)
  if ($res === false)
  {
 	$haderror = true;
-	$error = socket_strerror(socket_last_error());
-	$msg = "socket connect($addr,$port) failed";
-	$error = "ERR: $msg '$error'\n";
+	if ($rigipsecurity === false)
+	{
+		$error = socket_strerror(socket_last_error());
+		$msg = "socket connect($addr,$port) failed";
+		$error = "ERR: $msg '$error'\n";
+	}
+	else
+		$error = "ERR: socket connect($rig) failed\n";
+
 	socket_close($socket);
 	return null;
  }
@@ -365,12 +382,12 @@ function revert($str)
  return str_replace(array("\1", "\2", "\3", "\4"), array("|", "\\", "=", ","), $str);
 }
 #
-function api($cmd)
+function api($rig, $cmd)
 {
  global $haderror, $error;
  global $miner, $port, $hidefields;
 
- $socket = getsock($miner, $port);
+ $socket = getsock($rig, $miner, $port);
  if ($socket != null)
  {
 	socket_write($socket, $cmd, strlen($cmd));
@@ -506,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;
@@ -641,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;
@@ -713,16 +747,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
@@ -757,12 +801,15 @@ function fmt($section, $name, $value, $when, $alldata)
 	case 'total.Discarded':
 	case 'POOL.Diff1 Shares':
 	case 'total.Diff1 Shares':
+	case 'GPU.Diff1 Work':
+	case 'PGA.Diff1 Work':
+	case 'total.Diff1 Work':
 		$parts = explode('.', $value, 2);
 		if (count($parts) == 1)
 			$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':
@@ -1067,7 +1114,7 @@ function processgpus($rig)
  global $error;
  global $warnfont, $warnoff;
 
- $gpus = api('gpucount');
+ $gpus = api($rig, 'gpucount');
 
  if ($error != null)
 	otherrow("<td>Error getting GPU count: $warnfont$error$warnoff</td>");
@@ -1136,7 +1183,7 @@ function process($cmds, $rig)
  $count = count($cmds);
  foreach ($cmds as $cmd => $des)
  {
-	$process = api($cmd);
+	$process = api($rig, $cmd);
 
 	if ($error != null)
 	{
@@ -1251,7 +1298,7 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
 		else
 			$name = $num;
 
-		$ans = api($cmd);
+		$ans = api($name, $cmd);
 
 		if ($error != null)
 		{
@@ -1763,7 +1810,7 @@ function processcustompage($pagename, $sections, $sum, $namemap)
 
 		foreach ($cmds as $cmd => $one)
 		{
-			$process = api($cmd);
+			$process = api($name, $cmd);
 
 			if ($error != null)
 			{
@@ -2052,7 +2099,7 @@ function display()
 
  newtable();
  doforeach('version', 'rig summary', array(), array(), true);
- $sum = array('MHS av', 'Getworks', 'Found Blocks', 'Accepted', 'Rejected', 'Discarded', 'Stale', 'Utility', 'Local Work', 'Total MH', 'Work Utility', 'Diff1 Shares');
+ $sum = array('MHS av', 'Getworks', 'Found Blocks', 'Accepted', 'Rejected', 'Discarded', 'Stale', 'Utility', 'Local Work', 'Total MH', 'Work Utility', 'Diff1 Shares', 'Diff1 Work');
  doforeach('summary', 'summary information', $sum, array(), false);
  endtable();
  otherrow('<td><br><br></td>');

+ 72 - 1
util.c

@@ -305,7 +305,10 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, resp_hdr_cb);
 	curl_easy_setopt(curl, CURLOPT_HEADERDATA, &hi);
 	curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
-	if (opt_socks_proxy) {
+	if (pool->rpc_proxy) {
+		curl_easy_setopt(curl, CURLOPT_PROXY, pool->rpc_proxy);
+		curl_easy_setopt(curl, CURLOPT_PROXYTYPE, pool->rpc_proxytype);
+	} else if (opt_socks_proxy) {
 		curl_easy_setopt(curl, CURLOPT_PROXY, opt_socks_proxy);
 		curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
 	}
@@ -460,6 +463,68 @@ err_out:
 	return NULL;
 }
 
+#if (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 10) || (LIBCURL_VERSION_MAJOR > 7)
+static struct {
+	const char *name;
+	curl_proxytype proxytype;
+} proxynames[] = {
+	{ "http:",	CURLPROXY_HTTP },
+#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MINOR > 19) || (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH >= 4)
+	{ "http0:",	CURLPROXY_HTTP_1_0 },
+#endif
+#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MINOR > 15) || (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH >= 2)
+	{ "socks4:",	CURLPROXY_SOCKS4 },
+#endif
+	{ "socks5:",	CURLPROXY_SOCKS5 },
+#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MINOR >= 18)
+	{ "socks4a:",	CURLPROXY_SOCKS4A },
+	{ "socks5h:",	CURLPROXY_SOCKS5_HOSTNAME },
+#endif
+};
+#endif
+
+const char *proxytype(curl_proxytype proxytype)
+{
+	int i;
+
+	for (i = 0; proxynames[i].name; i++)
+		if (proxynames[i].proxytype == proxytype)
+			return proxynames[i].name;
+
+	return "invalid";
+}
+
+char *get_proxy(char *url, struct pool *pool)
+{
+	pool->rpc_proxy = NULL;
+
+#if (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 10) || (LIBCURL_VERSION_MAJOR > 7)
+	char *split;
+	int plen, len, i;
+
+	for (i = 0; proxynames[i].name; i++) {
+		plen = strlen(proxynames[i].name);
+		if (strncmp(url, proxynames[i].name, plen) == 0) {
+			if (!(split = strchr(url, '|')))
+				return url;
+
+			*split = '\0';
+			len = split - url;
+			pool->rpc_proxy = malloc(1 + len - plen);
+			if (!(pool->rpc_proxy))
+				quit(1, "Failed to malloc rpc_proxy");
+
+			strcpy(pool->rpc_proxy, url + plen);
+			pool->rpc_proxytype = proxynames[i].proxytype;
+			url = split + 1;
+			break;
+		}
+	}
+#endif
+	return url;
+}
+
+
 char *bin2hex(const unsigned char *p, size_t len)
 {
 	char *s = malloc((len * 2) + 1);
@@ -722,3 +787,9 @@ double us_tdiff(struct timeval *end, struct timeval *start)
 {
 	return end->tv_sec * 1000000 + end->tv_usec - start->tv_sec * 1000000 - start->tv_usec;
 }
+
+/* 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;
+}