Browse Source

Merge branch 'set_device_funcs' into bfgminer

Luke Dashjr 12 years ago
parent
commit
cd8069db6b
17 changed files with 602 additions and 599 deletions
  1. 0 2
      README
  2. 18 39
      README.ASIC
  3. 31 41
      README.FPGA
  4. 5 8
      README.GPU
  5. 25 14
      api.c
  6. 109 0
      deviceapi.c
  7. 16 0
      deviceapi.h
  8. 86 122
      driver-avalon.c
  9. 2 0
      driver-avalon.h
  10. 23 14
      driver-bitforce.c
  11. 1 1
      driver-bitfury.c
  12. 3 3
      driver-cairnsmore.c
  13. 107 152
      driver-icarus.c
  14. 23 7
      driver-klondike.c
  15. 11 1
      icarus-common.h
  16. 139 192
      miner.c
  17. 3 3
      miner.h

+ 0 - 2
README

@@ -274,9 +274,7 @@ Options for both config file and command line:
 --stratum-port <arg> Port number to listen on for stratum miners (-1 means disabled) (default: -1)
 --submit-threads    Minimum number of concurrent share submissions (default: 64)
 --syslog            Use system log for output messages (default: standard error)
---temp-cutoff <arg> Maximum temperature devices will be allowed to reach before being disabled, one value or comma separated list
 --temp-hysteresis <arg> Set how much the temperature can fluctuate outside limits when automanaging speeds (default: 3)
---temp-target <arg> Target temperature when automatically managing fan and clock speeds
 --text-only|-T      Disable ncurses formatted screen output
 --unicode           Use Unicode characters in TUI
 --url|-o <arg>      URL for bitcoin JSON-RPC server

+ 18 - 39
README.ASIC

@@ -9,7 +9,7 @@ KnCminer's Mercury, Jupiter and Saturn.
 AVALON
 ------
 
-Currently, Avalon boards are supported only by connecting them directly (or via
+Currently, Avalon boards are best supported by connecting them directly (or via
 a hub) to a regular PC running BFGMiner. It is also possible to install the
 OpenWrt packages of BFGMiner to the Avalon's embedded controller, but this is
 not a simple task due to its lack of available flash space.
@@ -19,31 +19,23 @@ First, add the -S option specifying the avalon driver specifically. For example,
 
 -S avalon:\\.\COM9
 
-Next, use the --avalon-options copying the command as used by the internal
-router used by the Avalon. eg:
-
---avalon-options 115200:24:10:45:282
-
-The values are baud : miners : asic count : timeout : frequency.
-
-Baud:
-The device is essentially hard coded to emulate 115200 baud so you shouldn't
-change this.
-
-Miners:
-Most Avalons are 3 module devices, which come to 24 miners. 4 module devices
-would use 32 here.
-
-Asic count:
-Virtually all have 10, so don't change this.
-
-Timeout:
-This defines how long the device will work on a work item before accepting new
-work to replace it. It should be changed according to the frequency (last
-setting).
-It is possible to set this a little lower if you are trying to tune for short
-block mining (eg p2pool) but much lower and the device will start creating
-duplicate shares.
+Next, use the --set-device option to provide the device configuration.
+If you are translating options from --avalon-options (cgminer and older versions
+of BFGMiner), note the values are baud:miner_count:asic_count:timeout:clock.
+
+    baud=N         The device is essentially hard coded to emulate 115200 baud,
+                   so you shouldn't change this.
+    miner_count=N  Most Avalons are 3 module devices, which come to 24 miners.
+                   4 module devices would use 32 here.
+    asic_count=N   Virtually all have 10, so don't change this.
+    timeout=N      This defines how long the device will work on a work item
+                   before accepting new work to replace it. It should be changed
+                   according to the frequency (last setting). It is possible to
+                   set this a little lower if you are trying to tune for short
+                   block mining (eg p2pool) but much lower and the device will
+                   start creating duplicate shares.
+    clock=N        This is the clock speed of the devices. Only specific values
+                   work: 256, 270, 282 (default), 300, 325, 350 and 375.
 
 Sample settings for valid different frequencies (last 2 values):
 34:375
@@ -54,19 +46,6 @@ Sample settings for valid different frequencies (last 2 values):
 47:270
 50:256
 
-Frequency:
-This is the clock speed of the devices. Only specific values work, 256, 270,
-282 (default), 300, 325, 350 and 375.
-
-If you use the full curses based interface with Avalons you will get this
-information:
-AVA 0: 22/ 46C  60%/2400R
-
-The values are:
-ambient temp / highest device temp  set fan % / lowest detected fan RPM.
-
-Check the API for more detailed information.
-
 
 BFSB, MEGABIGPOWER, AND METABANK BITFURY BOARDS
 -----------------------------------------------

+ 31 - 41
README.FPGA

@@ -136,44 +136,36 @@ be less than the firmware speed since you lose work on every block change.
 Icarus (ICA)
 ------------
 
-There are two hidden options in BFGMiner when Icarus support is compiled in:
-
---icarus-options <arg> Set specific FPGA board configurations - one set of values for all or comma separated
-           baud:work_division:fpga_count:quirks
-
-           baud           The Serial/USB baud rate - 115200 or 57600 only - default 115200
-           work_division  The fraction of work divided up for each FPGA chip - 1, 2, 4 or 8
-                          e.g. 2 means each FPGA does half the nonce range - default 2
-           fpga_count     The actual number of FPGA working - this would normally be the same
-                          as work_division - range is from 1 up to 'work_division'
-                          It defaults to the value of work_division - or 2 if you don't specify
-                          work_division
-           quirks         List of quirks to enable and disable (after a minus sign):
-                            r  Reopen device regularly to workaround buggy Icarus USB chipset
-                               (enabled by default)
-
-If you define fewer comma separated values than Icarus devices, the last values
-will be used for all extra devices.
-
-An example would be: --icarus-options 57600:2:1:-r
+There are a number of options for Icarus-compatible devices which can be used
+with --set-devices (or the RPC pgaset method):
+
+    baud=N           The serial baud rate (default 115200)
+    work_division=N  The fraction of work divided up for each processor: 1, 2,
+                     4, or 8. e.g. 2 means each does half the nonce range
+                     (default 2)
+    fpga_count=N     The actual number of processors working; this would
+                     normally be the same as work_division. Range is from 1 up
+                     to <work_division>. It defaults to the value of
+                     work_division, or 2 if you don't specify work_division.
+    reopen=MODE      Controls how often the driver reopens the device to
+                     workaround issues. Choices are 'never', on 'timeout' only
+                     (default), or every 'cycle'.
+    timing=MODE      Set how the timing is calculated:
+                         default[=N]   Use the default hash time
+                         short[=N]     Calculate the hash time and stop
+                                       adjusting it at ~315 difficulty 1 shares
+                                       (~1hr)
+                         long=[N]      Re-calculate the hash time continuously
+                         value[=N]     Specify the hash time in nanoseconds
+                                       (e.g. 2.6316) and abort time (e.g.
+                                       2.6316=80).
+
+An example would be: --set-device ECM:baud=57600 --set-device
+ECM:work_division=2 --set-device DCM:fpga_count=1 --set-device ECM:reopen=never
 This would mean: use 57600 baud, the FPGA board divides the work in half however
 only 1 FPGA actually runs on the board, and don't reopen the device (e.g. like
 an early CM1 Icarus copy bitstream).
 
---icarus-timing <arg> Set how the Icarus timing is calculated - one setting/value for all or comma separated
-           default[=N]   Use the default Icarus hash time (2.6316ns)
-           short=[N]     Calculate the hash time and stop adjusting it at ~315 difficulty 1 shares (~1hr)
-           long=[N]      Re-calculate the hash time continuously
-           value[=N]     Specify the hash time in nanoseconds (e.g. 2.6316) and abort time (e.g. 2.6316=80)
-
-If you define fewer comma separated values than Icarus devices, the last values
-will be used for all extra devices.
-
-Icarus timing is required for devices that do not exactly match a default
-Icarus Rev3 in processing speed.
-If you have an Icarus Rev3 you should not normally need to use --icarus-timing
-since the default values will maximise the Mh/s and display it correctly.
-
 Icarus timing is used to determine the number of hashes that have been checked
 when it aborts a nonce range (including on a longpoll).
 It is also used to determine the elapsed time when it should abort a nonce
@@ -181,8 +173,7 @@ range to avoid letting the Icarus go idle, but also to safely maximise that
 time.
 
 'short' or 'long' mode should only be used on a computer that has enough CPU
-available to run BFGMiner without any CPU delays (an active desktop or swapping
-computer would not be stable enough).
+available to run BFGMiner without any CPU delays.
 Any CPU delays while calculating the hash time will affect the result
 'short' mode only requires the computer to be stable until it has completed
 ~315 difficulty 1 shares, 'long' mode requires it to always be stable to ensure
@@ -211,11 +202,10 @@ time value (Hs) calculated.
 You can also use the RPC API 'stats' command to see the current hash time (Hs)
 at any time.
 
-The Icarus code currently only works with an FPGA device that supports the same
-commands as Icarus Rev3 requires and also is less than ~840Mh/s and greater
-than 2Mh/s.
-If an FPGA device does hash faster than ~840Mh/s it should work correctly if
-you supply the correct hash time nanoseconds value.
+The Icarus code currently only works with devices that support the same commands
+as Icarus Rev3 requires and also is less than ~840Mh/s and greater than 2Mh/s.
+If your device does hash faster than ~840Mh/s it should work correctly if you
+supply the correct hash time nanoseconds value.
 
 The timing code itself will affect the Icarus performance since it increases
 the delay after work is completed or aborted until it starts again.

+ 5 - 8
README.GPU

@@ -131,14 +131,13 @@ AUTO MODES:
 There are two "auto" modes in BFGMiner, --auto-fan and --auto-gpu. These can be
 used independently of each other and are complementary. Both auto modes are
 designed to safely change settings while trying to maintain a target
-temperature. By default this is set to 75 degrees C but can be changed with:
+temperature. By default this is set to 75 degrees C but can be changed with the
+--set-device option. For example:
 
---temp-target
-e.g.
---temp-target 80
+--set-device OCL:temp-target=80
 Sets all cards' target temperature to 80 degrees.
 
---temp-target 75,85
+--set-device OCL0:temp-target=75 --set-device OCL1:temp-target=85
 Sets card 0 target temperature to 75, and card 1 to 85 degrees.
 
 AUTO FAN:
@@ -185,9 +184,7 @@ cutoff limit (95 degrees by default), BFGMiner will completely disable the GPU
 from mining and it will not be re-enabled unless manually done so. The cutoff
 temperature can be changed with:
 
---temp-cutoff
-e.g.
---temp-cutoff 95,105
+--set-device OCL0:temp-cutoff=95 --set-device OCL1:temp-cutoff=105
 Sets card 0 cutoff temperature to 95 and card 1 to 105.
 
 --gpu-memdiff -125

+ 25 - 14
api.c

@@ -307,6 +307,8 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_INVNEG 121
 #define MSG_SETQUOTA 122
 
+#define USE_ALTMSG 0x4000
+
 enum code_severity {
 	SEVERITY_ERR,
 	SEVERITY_WARN,
@@ -484,6 +486,7 @@ struct CODES {
  { SEVERITY_WARN,  MSG_PGANOSET, PARAM_PGA,	"PGA %d does not support pgaset" },
  { SEVERITY_INFO,  MSG_PGAHELP, PARAM_BOTH,	"PGA %d set help: %s" },
  { SEVERITY_SUCC,  MSG_PGASETOK, PARAM_PGA,	"PGA %d set OK" },
+ { SEVERITY_SUCC,  MSG_PGASETOK | USE_ALTMSG, PARAM_BOTH,	"PGA %d set OK: %s" },
  { SEVERITY_ERR,   MSG_PGASETERR, PARAM_BOTH,	"PGA %d set failed: %s" },
 #endif
  { SEVERITY_ERR,   MSG_ZERMIS,	PARAM_NONE,	"Missing zero parameters" },
@@ -1157,7 +1160,7 @@ foundit:
 // All replies (except BYE and RESTART) start with a message
 //  thus for JSON, message() inserts JSON_START at the front
 //  and send_result() adds JSON_END at the end
-static void message(struct io_data *io_data, int messageid, int paramid, char *param2, bool isjson)
+static void message(struct io_data * const io_data, const int messageid2, const int paramid, const char * const param2, const bool isjson)
 {
 	struct api_data *root = NULL;
 	char buf[TMPBUFSIZ];
@@ -1170,6 +1173,7 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p
 	int cpu;
 #endif
 	int i;
+	int messageid = messageid2 & ~USE_ALTMSG;
 
 	io_reinit(io_data);
 
@@ -1177,7 +1181,7 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p
 		io_put(io_data, JSON_START JSON_STATUS);
 
 	for (i = 0; codes[i].severity != SEVERITY_FAIL; i++) {
-		if (codes[i].code == messageid) {
+		if (codes[i].code == messageid2) {
 			switch (codes[i].severity) {
 				case SEVERITY_WARN:
 					severity[0] = 'W';
@@ -3304,7 +3308,6 @@ static void setconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
 {
 	struct cgpu_info *cgpu;
-	struct device_drv *drv;
 	char buf[TMPBUFSIZ];
 	int numpga = numpgas();
 
@@ -3339,23 +3342,31 @@ static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe
 	}
 
 	cgpu = get_devices(dev);
-	drv = cgpu->drv;
 
 	char *set = strchr(opt, ',');
 	if (set)
 		*(set++) = '\0';
 
-	if (!drv->set_device)
-		message(io_data, MSG_PGANOSET, id, NULL, isjson);
-	else {
-		char *ret = drv->set_device(cgpu, opt, set, buf);
-		if (ret) {
-			if (strcasecmp(opt, "help") == 0)
-				message(io_data, MSG_PGAHELP, id, ret, isjson);
+	enum bfg_set_device_replytype success;
+	const char *ret = proc_set_device(cgpu, opt, set, buf, &success);
+	switch (success)
+	{
+		case SDR_HELP:
+			message(io_data, MSG_PGAHELP, id, ret, isjson);
+			break;
+		case SDR_OK:
+			if (ret)
+				message(io_data, MSG_PGASETOK | USE_ALTMSG, id, ret, isjson);
 			else
-				message(io_data, MSG_PGASETERR, id, ret, isjson);
-		} else
-			message(io_data, MSG_PGASETOK, id, NULL, isjson);
+				message(io_data, MSG_PGASETOK, id, NULL, isjson);
+			break;
+		case SDR_UNKNOWN:
+		case SDR_ERR:
+			message(io_data, MSG_PGASETERR, id, ret, isjson);
+			break;
+		case SDR_AUTO:
+		case SDR_NOSUPP:
+			message(io_data, MSG_PGANOSET, id, NULL, isjson);
 	}
 }
 #endif

+ 109 - 0
deviceapi.c

@@ -12,6 +12,7 @@
 
 #include "config.h"
 
+#include <ctype.h>
 #ifdef WIN32
 #include <winsock2.h>
 #else
@@ -857,6 +858,114 @@ bool add_cgpu_slave(struct cgpu_info *cgpu, struct cgpu_info *prev_cgpu)
 	return true;
 }
 
+const char *proc_set_device_help(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	const struct bfg_set_device_definition *sdf;
+	char *p = replybuf;
+	bool first = true;
+	
+	*out_success = SDR_HELP;
+	sdf = proc->set_device_funcs;
+	if (!sdf)
+nohelp:
+		return "No help available";
+	
+	size_t matchlen = 0;
+	if (newvalue)
+		while (!isspace(newvalue[0]))
+			++matchlen;
+	
+	for ( ; sdf->optname; ++sdf)
+	{
+		if (!sdf->description)
+			continue;
+		if (matchlen && (strncasecmp(optname, sdf->optname, matchlen) || optname[matchlen]))
+			continue;
+		if (first)
+			first = false;
+		else
+			p++[0] = '\n';
+		p += sprintf(p, "%s: %s", sdf->optname, sdf->description);
+	}
+	if (replybuf == p)
+		goto nohelp;
+	return replybuf;
+}
+
+const char *proc_set_device_temp_cutoff(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	int target_diff = proc->cutofftemp - proc->targettemp;
+	proc->cutofftemp = atoi(newvalue);
+	if (!proc->targettemp_user)
+		proc->targettemp = proc->cutofftemp - target_diff;
+	return NULL;
+}
+
+const char *proc_set_device_temp_target(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	proc->targettemp = atoi(newvalue);
+	proc->targettemp_user = true;
+	return NULL;
+}
+
+static inline
+void _set_auto_sdr(enum bfg_set_device_replytype * const out_success, const char * const rv, const char * const optname)
+{
+	if (!rv)
+		*out_success = SDR_OK;
+	else
+	if (!strcasecmp(optname, "help"))
+		*out_success = SDR_HELP;
+	else
+		*out_success = SDR_ERR;
+}
+
+const char *_proc_set_device(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	const struct bfg_set_device_definition *sdf;
+	
+	sdf = proc->set_device_funcs;
+	if (!sdf)
+	{
+		*out_success = SDR_NOSUPP;
+		return "Device does not support setting parameters.";
+	}
+	for ( ; sdf->optname; ++sdf)
+		if (!strcasecmp(optname, sdf->optname))
+		{
+			*out_success = SDR_AUTO;
+			const char * const rv = sdf->func(proc, optname, newvalue, replybuf, out_success);
+			if (SDR_AUTO == *out_success)
+				_set_auto_sdr(out_success, rv, optname);
+			return rv;
+		}
+	
+	if (!strcasecmp(optname, "temp-cutoff"))
+		return proc_set_device_temp_cutoff(proc, optname, newvalue, replybuf, out_success);
+	else
+	if (!strcasecmp(optname, "temp-target"))
+		return proc_set_device_temp_target(proc, optname, newvalue, replybuf, out_success);
+	else
+	if (!strcasecmp(optname, "help"))
+		return proc_set_device_help(proc, optname, newvalue, replybuf, out_success);
+	
+	*out_success = SDR_UNKNOWN;
+	sprintf(replybuf, "Unknown option: %s", optname);
+	return replybuf;
+}
+
+const char *proc_set_device(struct cgpu_info * const proc, char * const optname, char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	if (proc->drv->set_device)
+	{
+		const char * const rv = proc->drv->set_device(proc, optname, newvalue, replybuf);
+		_set_auto_sdr(out_success, rv, optname);
+		return rv;
+	}
+	
+	return _proc_set_device(proc, optname, newvalue, replybuf, out_success);
+}
+
 #ifdef NEED_BFG_LOWL_VCOM
 bool _serial_detect_all(struct lowlevel_device_info * const info, void * const userp)
 {

+ 16 - 0
deviceapi.h

@@ -77,6 +77,22 @@ extern void *miner_thread(void *);
 extern void add_cgpu_live(void*);
 extern bool add_cgpu_slave(struct cgpu_info *, struct cgpu_info *master);
 
+enum bfg_set_device_replytype {
+	SDR_AUTO,
+	SDR_OK,
+	SDR_ERR,
+	SDR_HELP,
+	SDR_UNKNOWN,
+	SDR_NOSUPP,
+};
+typedef const char *(*bfg_set_device_func_t)(struct cgpu_info *proc, const char *optname, const char *newvalue, char *replybuf, enum bfg_set_device_replytype *out_success);
+struct bfg_set_device_definition {
+	const char *optname;
+	bfg_set_device_func_t func;
+	const char *description;
+};
+extern const char *proc_set_device(struct cgpu_info *proc, char *optname, char *newvalue, char *replybuf, enum bfg_set_device_replytype *out_success);
+
 typedef bool(*detectone_func_t)(const char*);
 typedef int(*autoscan_func_t)();
 

+ 86 - 122
driver-avalon.c

@@ -45,8 +45,6 @@
 
 BFG_REGISTER_DRIVER(avalon_drv)
 
-static int option_offset = -1;
-
 static int avalon_init_task(struct avalon_task *at,
 			    uint8_t reset, uint8_t ff, uint8_t fan,
 			    uint8_t timeout, uint8_t asic_num,
@@ -419,119 +417,80 @@ static void avalon_idle(struct cgpu_info *avalon)
 	applog(LOG_ERR, "Avalon: Goto idle mode");
 }
 
-static void get_options(int this_option_offset, int *baud, int *miner_count,
-			int *asic_count, int *timeout, int *frequency)
+static
+const char *avalon_set_baud(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
 {
-	char buf[BUFSIZ+1];
-	char *ptr, *comma, *colon, *colon2, *colon3, *colon4;
-	size_t max;
-	int i, tmp;
-
-	if (opt_avalon_options == NULL)
-		buf[0] = '\0';
-	else {
-		ptr = opt_avalon_options;
-		for (i = 0; i < this_option_offset; i++) {
-			comma = strchr(ptr, ',');
-			if (comma == NULL)
-				break;
-			ptr = comma + 1;
-		}
-
-		comma = strchr(ptr, ',');
-		if (comma == NULL)
-			max = strlen(ptr);
-		else
-			max = comma - ptr;
-
-		if (max > BUFSIZ)
-			max = BUFSIZ;
-		strncpy(buf, ptr, max);
-		buf[max] = '\0';
-	}
-
-	*baud = AVALON_IO_SPEED;
-	*miner_count = AVALON_DEFAULT_MINER_NUM - 8;
-	*asic_count = AVALON_DEFAULT_ASIC_NUM;
-	*timeout = AVALON_DEFAULT_TIMEOUT;
-	*frequency = AVALON_DEFAULT_FREQUENCY;
+	struct avalon_info * const info = proc->device_data;
+	const int baud = atoi(newvalue);
+	if (!valid_baud(baud))
+		return "Invalid baud setting";
+	info->baud = baud;
+	return NULL;
+}
 
-	if (!(*buf))
-		return;
+static
+const char *avalon_set_miner_count(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct avalon_info * const info = proc->device_data;
+	const int miner_count = atoi(newvalue);
+	if (miner_count <= 0 || miner_count > AVALON_DEFAULT_MINER_NUM)
+		return "Invalid miner_count: must be 1 ~ " AVALON_DEFAULT_MINER_NUM_S;
+	info->miner_count = miner_count;
+	return NULL;
+}
 
-	colon = strchr(buf, ':');
-	if (colon)
-		*(colon++) = '\0';
-
-	tmp = atoi(buf);
-	if (!valid_baud(*baud = tmp))
-		quit(1, "Invalid avalon-options for baud (%s)", buf);
-
-	if (colon && *colon) {
-		colon2 = strchr(colon, ':');
-		if (colon2)
-			*(colon2++) = '\0';
-
-		if (*colon) {
-			tmp = atoi(colon);
-			if (tmp > 0 && tmp <= AVALON_DEFAULT_MINER_NUM) {
-				*miner_count = tmp;
-			} else {
-				quit(1, "Invalid avalon-options for "
-					"miner_count (%s) must be 1 ~ %d",
-					colon, AVALON_DEFAULT_MINER_NUM);
-			}
-		}
+static
+const char *avalon_set_asic_count(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct avalon_info * const info = proc->device_data;
+	const int asic_count = atoi(newvalue);
+	if (asic_count <= 0 || asic_count > AVALON_DEFAULT_ASIC_NUM)
+		return "Invalid asic_count: must be 1 ~ " AVALON_DEFAULT_ASIC_NUM_S;
+	info->asic_count = asic_count;
+	return NULL;
+}
 
-		if (colon2 && *colon2) {
-			colon3 = strchr(colon2, ':');
-			if (colon3)
-				*(colon3++) = '\0';
-
-			tmp = atoi(colon2);
-			if (tmp > 0 && tmp <= AVALON_DEFAULT_ASIC_NUM)
-				*asic_count = tmp;
-			else {
-				quit(1, "Invalid avalon-options for "
-					"asic_count (%s) must be 1 ~ %d",
-					colon2, AVALON_DEFAULT_ASIC_NUM);
-			}
+static
+const char *avalon_set_timeout(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct avalon_info * const info = proc->device_data;
+	const int timeout = atoi(newvalue);
+	if (timeout <= 0 || timeout > 0xff)
+		return "Invalid timeout: must be 1 ~ 255";
+	info->timeout = timeout;
+	return NULL;
+}
 
-			if (colon3 && *colon3) {
-				colon4 = strchr(colon3, ':');
-				if (colon4)
-					*(colon4++) = '\0';
-
-				tmp = atoi(colon3);
-				if (tmp > 0 && tmp <= 0xff)
-					*timeout = tmp;
-				else {
-					quit(1, "Invalid avalon-options for "
-						"timeout (%s) must be 1 ~ %d",
-						colon3, 0xff);
-				}
-				if (colon4 && *colon4) {
-					tmp = atoi(colon4);
-					switch (tmp) {
-					case 256:
-					case 270:
-					case 282:
-					case 300:
-					case 325:
-					case 350:
-					case 375:
-						*frequency = tmp;
-						break;
-					default:
-						quit(1, "Invalid avalon-options for "
-							"frequency must be 256/270/282/300/325/350/375");
-					}
-				}
-			}
-		}
+static
+const char *avalon_set_clock(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct avalon_info * const info = proc->device_data;
+	const int clock = atoi(newvalue);
+	switch (clock) {
+		default:
+			return "Invalid clock: must be 256/270/282/300/325/350/375";
+		case 256:
+		case 270:
+		case 282:
+		case 300:
+		case 325:
+		case 350:
+		case 375:
+			info->frequency = clock;
 	}
+	return NULL;
 }
 
+const struct bfg_set_device_definition avalon_set_device_funcs[] = {
+	// NOTE: Order of parameters below is important for --avalon-options
+	{"baud"       , avalon_set_baud       , "serial baud rate"},
+	{"miner_count", avalon_set_miner_count, ""},
+	{"asic_count" , avalon_set_asic_count , ""},
+	{"timeout"    , avalon_set_timeout    , "how long the device will work on a work item before accepting new work"},
+	{"clock"      , avalon_set_clock      , "clock speed: 256, 270, 282, 300, 325, 350, or 375"},
+	{NULL},
+};
+
 /* Non blocking clearing of anything in the buffer */
 static void avalon_clear_readbuf(int fd)
 {
@@ -559,23 +518,31 @@ static bool avalon_detect_one(const char *devpath)
 	struct avalon_info *info;
 	struct avalon_result ar;
 	int fd, ret;
-	int baud, miner_count, asic_count, timeout, frequency = 0;
 	struct cgpu_info *avalon;
 
 	if (serial_claim(devpath, &avalon_drv))
 		return false;
 	
-	int this_option_offset = ++option_offset;
-	get_options(this_option_offset, &baud, &miner_count, &asic_count,
-		    &timeout, &frequency);
+	info = malloc(sizeof(*info));
+	if (unlikely(!info))
+		applogr(false, LOG_ERR, "Failed to malloc avalon_info data");
+	*info = (struct avalon_info){
+		.baud = AVALON_IO_SPEED,
+		.miner_count = AVALON_DEFAULT_MINER_NUM - 8,
+		.asic_count = AVALON_DEFAULT_ASIC_NUM,
+		.timeout = AVALON_DEFAULT_TIMEOUT,
+		.frequency = AVALON_DEFAULT_FREQUENCY,
+	};
+	drv_set_defaults(&avalon_drv, avalon_set_device_funcs, info, devpath, detectone_meta_info.serial, 1);
 
 	applog(LOG_DEBUG, "Avalon Detect: Attempting to open %s "
 	       "(baud=%d miner_count=%d asic_count=%d timeout=%d frequency=%d)",
-	       devpath, baud, miner_count, asic_count, timeout, frequency);
+	       devpath, info->baud, info->miner_count, info->asic_count, info->timeout, info->frequency);
 
-	fd = avalon_open2(devpath, baud, true);
+	fd = avalon_open2(devpath, info->baud, true);
 	if (unlikely(fd == -1)) {
 		applog(LOG_ERR, "Avalon Detect: Failed to open %s", devpath);
+		free(info);
 		return false;
 	}
 	avalon_clear_readbuf(fd);
@@ -586,27 +553,22 @@ static bool avalon_detect_one(const char *devpath)
 	avalon->device_path = strdup(devpath);
 	avalon->device_fd = fd;
 	avalon->threads = AVALON_MINER_THREADS;
+	avalon->set_device_funcs = avalon_set_device_funcs;
 	add_cgpu(avalon);
 
 	ret = avalon_reset(fd, &ar);
 	if (ret) {
 		; /* FIXME: I think IT IS avalon and wait on reset;
 		   * avalon_close(fd);
+		   * free(info);
 		   * return false; */
 	}
 	
 	applog(LOG_INFO, "Avalon Detect: Found at %s, mark as %d",
 	       devpath, avalon->device_id);
 
-	avalon->device_data = calloc(sizeof(struct avalon_info), 1);
-	if (unlikely(!(avalon->device_data)))
-		quit(1, "Failed to malloc avalon_info data");
-	info = avalon->device_data;
+	avalon->device_data = info;
 
-	info->baud = baud;
-	info->miner_count = miner_count;
-	info->asic_count = asic_count;
-	info->timeout = timeout;
 	info->read_count = ((float)info->timeout * AVALON_HASH_TIME_FACTOR *
 			    AVALON_TIME_FACTOR) / (float)info->miner_count;
 
@@ -620,7 +582,6 @@ static bool avalon_detect_one(const char *devpath)
 	info->temp_history_index = 0;
 	info->temp_sum = 0;
 	info->temp_old = 0;
-	info->frequency = frequency;
 
 	/* Set asic to idle mode after detect */
 	avalon_idle(avalon);
@@ -646,6 +607,9 @@ static void avalon_init(struct cgpu_info *avalon)
 	struct avalon_info *info = avalon->device_data;
 	struct avalon_result ar;
 	int fd, ret;
+	
+	cgpu_set_defaults(avalon);
+	avalon->set_device_funcs = NULL;
 
 	avalon->device_fd = -1;
 	fd = avalon_open(avalon->device_path, info->baud);

+ 2 - 0
driver-avalon.h

@@ -30,7 +30,9 @@
 #define AVALON_DEFAULT_TIMEOUT 0x32
 #define AVALON_DEFAULT_FREQUENCY 256
 #define AVALON_DEFAULT_MINER_NUM 0x20
+#define AVALON_DEFAULT_MINER_NUM_S "32"
 #define AVALON_DEFAULT_ASIC_NUM 0xA
+#define AVALON_DEFAULT_ASIC_NUM_S "10"
 
 #define AVALON_FTDI_READSIZE 512
 

+ 23 - 14
driver-bitforce.c

@@ -64,6 +64,7 @@ static const char *protonames[] = {
 
 BFG_REGISTER_DRIVER(bitforce_drv)
 BFG_REGISTER_DRIVER(bitforce_queue_api)
+static const struct bfg_set_device_definition bitforce_set_device_funcs[];
 
 // Code must deal with a timeout
 #define BFopen(devpath)  serial_open(devpath, 0, 250, true)
@@ -315,6 +316,9 @@ static bool bitforce_detect_one(const char *devpath)
 		bitforce->name = strdup(pdevbuf + 7);
 	}
 	bitforce->device_data = initdata;
+	
+	// Skip fanspeed until we probe support for it
+	bitforce->set_device_funcs = &bitforce_set_device_funcs[1];
 
 	mutex_init(&bitforce->device_mutex);
 
@@ -607,7 +611,10 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 		{
 			bitforce_cmd1(fdDev, data->xlink_id, voltbuf, sizeof(voltbuf), "Z9X");
 			if (strncasecmp(voltbuf, "ERR", 3))
+			{
 				data->supports_fanspeed = true;
+				bitforce->set_device_funcs = bitforce_set_device_funcs;
+			}
 			data->probed = true;
 		}
 		bitforce_cmd1(fdDev, data->xlink_id, voltbuf, sizeof(voltbuf), "ZTX");
@@ -1537,19 +1544,12 @@ void bitforce_poll(struct thr_info *thr)
 }
 
 static
-char *bitforce_set_device(struct cgpu_info *proc, char *option, char *setting, char *replybuf)
+const char *bitforce_set_fanmode(struct cgpu_info * const proc, const char * const option, const char * const setting, char * const replybuf, enum bfg_set_device_replytype * const success)
 {
 	struct bitforce_data *data = proc->device_data;
 	pthread_mutex_t *mutexp = &proc->device->device_mutex;
 	int fd;
 	
-	if (!strcasecmp(option, "help"))
-	{
-		sprintf(replybuf, "fanmode: range 0-5 (low to fast) or 9 (auto)");
-		return replybuf;
-	}
-	
-	if (!strcasecmp(option, "fanmode"))
 	{
 		if (!data->supports_fanspeed)
 		{
@@ -1575,20 +1575,31 @@ char *bitforce_set_device(struct cgpu_info *proc, char *option, char *setting, c
 		mutex_unlock(mutexp);
 		return replybuf;
 	}
+}
+
+static
+const char *bitforce_rpc_send_cmd1(struct cgpu_info * const proc, const char * const option, const char * const setting, char * const replybuf, enum bfg_set_device_replytype * const success)
+{
+	struct bitforce_data *data = proc->device_data;
+	pthread_mutex_t *mutexp = &proc->device->device_mutex;
+	int fd;
 	
-	if (!strcasecmp(option, "_cmd1"))
 	{
 		mutex_lock(mutexp);
 		fd = proc->device->device_fd;
 		bitforce_cmd1b(fd, data->xlink_id, replybuf, 8000, setting, strlen(setting));
 		mutex_unlock(mutexp);
+		*success = SDR_OK;
 		return replybuf;
 	}
-	
-	sprintf(replybuf, "Unknown option: %s", option);
-	return replybuf;
 }
 
+static const struct bfg_set_device_definition bitforce_set_device_funcs[] = {
+	{"fanmode", bitforce_set_fanmode, "range 0-5 (low to fast) or 9 (auto)"},
+	{"_cmd1", bitforce_rpc_send_cmd1, NULL},
+	{NULL},
+};
+
 struct device_drv bitforce_drv = {
 	.dname = "bitforce",
 	.name = "BFL",
@@ -1603,7 +1614,6 @@ struct device_drv bitforce_drv = {
 	.minerloop = minerloop_async,
 	.reinit_device = bitforce_reinit,
 	.get_stats = bitforce_get_stats,
-	.set_device = bitforce_set_device,
 	.identify_device = bitforce_identify,
 	.thread_prepare = bitforce_thread_prepare,
 	.thread_init = bitforce_thread_init,
@@ -2118,7 +2128,6 @@ struct device_drv bitforce_queue_api = {
 #endif
 	.get_api_stats = bitforce_drv_stats,
 	.get_stats = bitforce_get_stats,
-	.set_device = bitforce_set_device,
 	.identify_device = bitforce_identify,
 	.thread_prepare = bitforce_thread_prepare,
 	.thread_init = bitforce_thread_init,

+ 1 - 1
driver-bitfury.c

@@ -62,7 +62,7 @@ int bitfury_autodetect()
 		struct bitfury_device dummy_bitfury = {
 			.spi = sys_spi,
 		};
-		drv_set_defaults(&bitfury_drv, bitfury_spi_port_config, &dummy_bitfury);
+		drv_set_defaults(&bitfury_drv, bitfury_spi_port_config, &dummy_bitfury, NULL, NULL, 0);
 	}
 	
 	chip_n = libbitfury_detectChips1(sys_spi);

+ 3 - 3
driver-cairnsmore.c

@@ -45,7 +45,7 @@ static bool cairnsmore_detect_one(const char *devpath)
 	info->baud = CAIRNSMORE1_IO_SPEED;
 	info->work_division = 2;
 	info->fpga_count = 2;
-	info->quirk_reopen = 0;
+	info->reopen_mode = IRM_NEVER;
 	info->Hs = CAIRNSMORE1_HASH_TIME;
 	info->timing_mode = MODE_LONG;
 	info->do_icarus_timing = true;
@@ -147,9 +147,9 @@ static bool cairnsmore_init(struct thr_info *thr)
 		       CAIRNSMORE1_DEFAULT_CLOCK, CAIRNSMORE1_MINIMUM_CLOCK, CAIRNSMORE1_MAXIMUM_CLOCK
 		);
 		// The dynamic-clocking firmware connects each FPGA as its own device
-		if (!(info->user_set & 1)) {
+		if (!(info->user_set & IUS_WORK_DIVISION)) {
 			info->work_division = 1;
-			if (!(info->user_set & 2))
+			if (!(info->user_set & IUS_FPGA_COUNT))
 				info->fpga_count = 1;
 		}
 	} else {

+ 107 - 152
driver-icarus.c

@@ -146,24 +146,8 @@ static const char *MODE_UNKNOWN_STR = "unknown";
 #define END_CONDITION 0x0000ffff
 #define DEFAULT_DETECT_THRESHOLD 1
 
-// Looking for options in --icarus-timing and --icarus-options:
-//
-// Code increments this each time we start to look at a device
-// However, this means that if other devices are checked by
-// the Icarus code (e.g. BFL) they will count in the option offset
-//
-// This, however, is deterministic so that's OK
-//
-// If we were to increment after successfully finding an Icarus
-// that would be random since an Icarus may fail and thus we'd
-// not be able to predict the option order
-//
-// This also assumes that serial_detect() checks them sequentially
-// and in the order specified on the command line
-//
-static int option_offset = -1;
-
 BFG_REGISTER_DRIVER(icarus_drv)
+extern const struct bfg_set_device_definition icarus_set_device_funcs[];
 
 extern void convert_icarus_to_cairnsmore(struct cgpu_info *);
 
@@ -317,44 +301,17 @@ static const char *timing_mode_str(enum timing_mode timing_mode)
 	}
 }
 
-static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
+static
+const char *icarus_set_timing(struct cgpu_info * const proc, const char * const optname, const char * const buf, char * const replybuf, enum bfg_set_device_replytype * const out_success)
 {
-	struct ICARUS_INFO *info = icarus->device_data;
+	struct ICARUS_INFO * const info = proc->device_data;
 	double Hs;
-	char buf[BUFSIZ+1];
-	char *ptr, *comma, *eq;
-	size_t max;
-	int i;
-
-	if (opt_icarus_timing == NULL)
-		buf[0] = '\0';
-	else {
-		ptr = opt_icarus_timing;
-		for (i = 0; i < this_option_offset; i++) {
-			comma = strchr(ptr, ',');
-			if (comma == NULL)
-				break;
-			ptr = comma + 1;
-		}
-
-		comma = strchr(ptr, ',');
-		if (comma == NULL)
-			max = strlen(ptr);
-		else
-			max = comma - ptr;
-
-		if (max > BUFSIZ)
-			max = BUFSIZ;
-		strncpy(buf, ptr, max);
-		buf[max] = '\0';
-	}
-
-	info->read_count = 0;
-	info->read_count_limit = 0; // 0 = no limit
+	char *eq;
 
 	if (strcasecmp(buf, MODE_SHORT_STR) == 0) {
 		// short
 		info->read_count = ICARUS_READ_COUNT_TIMING;
+		info->read_count_limit = 0;  // 0 = no limit
 
 		info->timing_mode = MODE_SHORT;
 		info->do_icarus_timing = true;
@@ -373,6 +330,7 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 	} else if (strcasecmp(buf, MODE_LONG_STR) == 0) {
 		// long
 		info->read_count = ICARUS_READ_COUNT_TIMING;
+		info->read_count_limit = 0;  // 0 = no limit
 
 		info->timing_mode = MODE_LONG;
 		info->do_icarus_timing = true;
@@ -393,6 +351,7 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 		info->Hs = Hs / NANOSEC;
 		info->fullnonce = info->Hs * (((double)0xffffffff) + 1);
 
+		info->read_count = 0;
 		if ((eq = strchr(buf, '=')) != NULL)
 			info->read_count = atoi(eq+1);
 
@@ -402,6 +361,8 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 		if (unlikely(info->read_count < 1))
 			info->read_count = 1;
 
+		info->read_count_limit = 0;  // 0 = no limit
+		
 		info->timing_mode = MODE_VALUE;
 		info->do_icarus_timing = false;
 	} else {
@@ -409,13 +370,14 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 
 		info->fullnonce = info->Hs * (((double)0xffffffff) + 1);
 
+		info->read_count = 0;
 		if ((eq = strchr(buf, '=')) != NULL)
 			info->read_count = atoi(eq+1);
 
 		int def_read_count = ICARUS_READ_COUNT_TIMING;
 
 		if (info->timing_mode == MODE_DEFAULT) {
-			if (icarus->drv == &icarus_drv) {
+			if (proc->drv == &icarus_drv) {
 				info->do_default_detection = 0x10;
 			} else {
 				def_read_count = (int)(info->fullnonce * TIME_FACTOR) - 1;
@@ -425,14 +387,18 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 		}
 		if (info->read_count < 1)
 			info->read_count = def_read_count;
+		
+		info->read_count_limit = 0;  // 0 = no limit
 	}
 
 	info->min_data_count = MIN_DATA_COUNT;
 
 	applog(LOG_DEBUG, "%"PRIpreprv": Init: mode=%s read_count=%d limit=%dms Hs=%e",
-		icarus->proc_repr,
+		proc->proc_repr,
 		timing_mode_str(info->timing_mode),
 		info->read_count, info->read_count_limit, info->Hs);
+	
+	return NULL;
 }
 
 static uint32_t mask(int work_division)
@@ -454,102 +420,12 @@ static uint32_t mask(int work_division)
 		nonce_mask = 0x1fffffff;
 		break;
 	default:
-		quit(1, "Invalid2 icarus-options for work_division (%d) must be 1, 2, 4 or 8", work_division);
+		quit(1, "Invalid2 work_division (%d) must be 1, 2, 4 or 8", work_division);
 	}
 
 	return nonce_mask;
 }
 
-static void get_options(int this_option_offset, struct ICARUS_INFO *info)
-{
-	int *baud = &info->baud;
-	int *work_division = &info->work_division;
-	int *fpga_count = &info->fpga_count;
-
-	char buf[BUFSIZ+1];
-	char *ptr, *comma, *colon, *colon2;
-	size_t max;
-	int i, tmp;
-
-	if (opt_icarus_options == NULL)
-		buf[0] = '\0';
-	else {
-		ptr = opt_icarus_options;
-		for (i = 0; i < this_option_offset; i++) {
-			comma = strchr(ptr, ',');
-			if (comma == NULL)
-				break;
-			ptr = comma + 1;
-		}
-
-		comma = strchr(ptr, ',');
-		if (comma == NULL)
-			max = strlen(ptr);
-		else
-			max = comma - ptr;
-
-		if (max > BUFSIZ)
-			max = BUFSIZ;
-		strncpy(buf, ptr, max);
-		buf[max] = '\0';
-	}
-
-	if (*buf) {
-		colon = strchr(buf, ':');
-		if (colon)
-			*(colon++) = '\0';
-
-		if (*buf) {
-			tmp = atoi(buf);
-			if (!valid_baud(*baud = tmp))
-				quit(1, "Invalid icarus-options for baud (%s)", buf);
-		}
-
-		if (colon && *colon) {
-			colon2 = strchr(colon, ':');
-			if (colon2)
-				*(colon2++) = '\0';
-
-			if (*colon) {
-				info->user_set |= 1;
-				tmp = atoi(colon);
-				if (tmp == 1 || tmp == 2 || tmp == 4 || tmp == 8) {
-					*work_division = tmp;
-					*fpga_count = tmp;	// default to the same
-				} else {
-					quit(1, "Invalid icarus-options for work_division (%s) must be 1, 2, 4 or 8", colon);
-				}
-			}
-
-			if (colon2 && *colon2) {
-			  colon = strchr(colon2, ':');
-			  if (colon)
-					*(colon++) = '\0';
-
-			  if (*colon2) {
-				info->user_set |= 2;
-				tmp = atoi(colon2);
-				if (tmp > 0 && tmp <= *work_division)
-					*fpga_count = tmp;
-				else {
-					quit(1, "Invalid icarus-options for fpga_count (%s) must be >0 and <=work_division (%d)", colon2, *work_division);
-				}
-			  }
-
-			  if (colon && *colon) {
-					colon2 = strchr(colon, '-') ?: "";
-					if (*colon2)
-						*(colon2++) = '\0';
-					if (strchr(colon, 'r'))
-						info->quirk_reopen = 2;
-					if (strchr(colon2, 'r'))
-						info->quirk_reopen = 0;
-			  }
-			}
-		}
-	}
-}
-
 // Number of bytes remaining after reading a nonce from Icarus
 int icarus_excess_nonce_size(int fd, struct ICARUS_INFO *info)
 {
@@ -573,8 +449,6 @@ int icarus_excess_nonce_size(int fd, struct ICARUS_INFO *info)
 
 bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct ICARUS_INFO *info)
 {
-	int this_option_offset = ++option_offset;
-
 	struct timeval tv_start, tv_finish;
 	int fd;
 
@@ -599,7 +473,7 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 	unsigned char ob_bin[64], nonce_bin[ICARUS_NONCE_SIZE];
 	char nonce_hex[(sizeof(nonce_bin) * 2) + 1];
 
-	get_options(this_option_offset, info);
+	drv_set_defaults(api, icarus_set_device_funcs, info, devpath, detectone_meta_info.serial, 1);
 
 	int baud = info->baud;
 	int work_division = info->work_division;
@@ -666,6 +540,7 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 	icarus->device_path = strdup(devpath);
 	icarus->device_fd = -1;
 	icarus->threads = 1;
+	icarus->set_device_funcs = icarus_set_device_funcs;
 	add_cgpu(icarus);
 
 	applog(LOG_INFO, "Found %"PRIpreprv" at %s",
@@ -680,8 +555,6 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 
 	timersub(&tv_finish, &tv_start, &(info->golden_tv));
 
-	set_timing_mode(this_option_offset, icarus);
-
 	return true;
 }
 
@@ -695,7 +568,7 @@ static bool icarus_detect_one(const char *devpath)
 	// if they support them and if setting them makes any difference
 	// N.B. B3000000 doesn't work on Icarus
 	info->baud = ICARUS_IO_SPEED;
-	info->quirk_reopen = 1;
+	info->reopen_mode = IRM_TIMEOUT;
 	info->Hs = ICARUS_REV3_HASH_TIME;
 	info->timing_mode = MODE_DEFAULT;
 	info->read_size = ICARUS_DEFAULT_READ_SIZE;
@@ -1050,7 +923,7 @@ keepwaiting:
 						return -1;
 					break;
 				case ICA_GETS_TIMEOUT:
-					if (info->quirk_reopen == 1 && !icarus_reopen(icarus, state, &fd))
+					if (info->reopen_mode == IRM_TIMEOUT && !icarus_reopen(icarus, state, &fd))
 						return -1;
 				case ICA_GETS_OK:
 					break;
@@ -1109,8 +982,13 @@ keepwaiting:
 			dclk_errorCount(&info->dclk, qsec);
 	}
 	
-	// Force a USB close/reopen on any hw error
-	if (was_hw_error && info->quirk_reopen != 2) {
+	// Force a USB close/reopen on any hw error (or on request, eg for baud change)
+	if (was_hw_error || info->reopen_now)
+	{
+		info->reopen_now = false;
+		if (info->reopen_mode == IRM_CYCLE)
+		{}  // Do nothing here, we reopen after sending the job
+		else
 		if (!icarus_reopen(icarus, state, &fd))
 			state->firstrun = true;
 	}
@@ -1123,7 +1001,7 @@ keepwaiting:
 	if (unlikely(icarus->deven != DEV_ENABLED || !icarus_job_start(thr)))
 		state->firstrun = true;
 
-	if (info->quirk_reopen == 2 && !icarus_reopen(icarus, state, &fd))
+	if (info->reopen_mode == IRM_CYCLE && !icarus_reopen(icarus, state, &fd))
 		state->firstrun = true;
 
 	work->blk.nonce = 0xffffffff;
@@ -1353,12 +1231,89 @@ static struct api_data *icarus_drv_stats(struct cgpu_info *cgpu)
 	return root;
 }
 
+static
+const char *icarus_set_baud(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct ICARUS_INFO * const info = proc->device_data;
+	const int baud = atoi(newvalue);
+	if (!valid_baud(baud))
+		return "Invalid baud setting";
+	if (info->baud != baud)
+	{
+		info->baud = baud;
+		info->reopen_now = true;
+	}
+	return NULL;
+}
+
+static
+const char *icarus_set_work_division(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct ICARUS_INFO * const info = proc->device_data;
+	const int work_division = atoi(newvalue);
+	if (!(work_division == 1 || work_division == 2 || work_division == 4 || work_division == 8))
+		return "Invalid work_division: must be 1, 2, 4 or 8";
+	if (info->user_set & IUS_FPGA_COUNT)
+	{
+		if (info->fpga_count > work_division)
+			return "work_division must be >= fpga_count";
+	}
+	else
+		info->fpga_count = work_division;
+	info->user_set |= IUS_WORK_DIVISION;
+	info->work_division = work_division;
+	info->nonce_mask = mask(work_division);
+	return NULL;
+}
+
+static
+const char *icarus_set_fpga_count(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct ICARUS_INFO * const info = proc->device_data;
+	const int fpga_count = atoi(newvalue);
+	if (fpga_count < 1 || fpga_count > info->work_division)
+		return "Invalid fpga_count: must be >0 and <=work_division";
+	info->fpga_count = fpga_count;
+	return NULL;
+}
+
+static
+const char *icarus_set_reopen(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct ICARUS_INFO * const info = proc->device_data;
+	if ((!strcasecmp(newvalue, "never")) || !strcasecmp(newvalue, "-r"))
+		info->reopen_mode = IRM_NEVER;
+	else
+	if (!strcasecmp(newvalue, "timeout"))
+		info->reopen_mode = IRM_TIMEOUT;
+	else
+	if ((!strcasecmp(newvalue, "cycle")) || !strcasecmp(newvalue, "r"))
+		info->reopen_mode = IRM_CYCLE;
+	else
+	if (!strcasecmp(newvalue, "now"))
+		info->reopen_now = true;
+	else
+		return "Invalid reopen mode";
+	return NULL;
+}
+
 static void icarus_shutdown(struct thr_info *thr)
 {
 	do_icarus_close(thr);
 	free(thr->cgpu_data);
 }
 
+const struct bfg_set_device_definition icarus_set_device_funcs[] = {
+	// NOTE: Order of parameters below is important for --icarus-options
+	{"baud"         , icarus_set_baud         , "serial baud rate"},
+	{"work_division", icarus_set_work_division, "number of pieces work is split into"},
+	{"fpga_count"   , icarus_set_fpga_count   , "number of chips working on pieces"},
+	{"reopen"       , icarus_set_reopen       , "how often to reopen device: never, timeout, cycle, (or now for a one-shot reopen)"},
+	// NOTE: Below here, order is irrelevant
+	{"timing"       , icarus_set_timing       , "timing of device; see README.FPGA"},
+	{NULL},
+};
+
 struct device_drv icarus_drv = {
 	.dname = "icarus",
 	.name = "ICA",

+ 23 - 7
driver-klondike.c

@@ -253,6 +253,7 @@ struct klondike_info {
 	int wque_size;
 	int wque_cleared;
 
+	int clock;
 	bool initialised;
 	
 	struct libusb_device_handle *usbdev_handle;
@@ -747,6 +748,7 @@ static bool klondike_init(struct cgpu_info *klncgpu)
 	int slaves, dev;
 
 	klninfo->initialised = false;
+	cgpu_set_defaults(klncgpu);
 
 	zero_kline(&kline);
 	kline.hd.cmd = KLN_CMD_STATUS;
@@ -785,13 +787,9 @@ static bool klondike_init(struct cgpu_info *klncgpu)
 	int size = 2;
 
 	// boundaries are checked by device, with valid values returned
-	if (opt_klondike_options != NULL) {
-		int hashclock;
-		double temptarget;
-
-		sscanf(opt_klondike_options, "%d:%lf", &hashclock, &temptarget);
-		SET_HASHCLOCK(kline.cfg.hashclock, hashclock);
-		kline.cfg.temptarget = cvtCToKln(temptarget);
+	{
+		SET_HASHCLOCK(kline.cfg.hashclock, klninfo->clock);
+		kline.cfg.temptarget = cvtCToKln(klncgpu->targettemp);
 		kline.cfg.tempcritical = 0; // hard code for old firmware
 		kline.cfg.fantarget = 0xff; // hard code for old firmware
 		size = sizeof(kline.cfg) - 2;
@@ -842,6 +840,21 @@ static void control_init(struct cgpu_info *klncgpu)
 			  klncgpu->drv->name, klncgpu->device_id, err);
 }
 
+static
+const char *klondike_set_clock(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct klondike_info * const klninfo = proc->device_data;
+	if (klninfo->initialised)
+		return "Cannot change clock after initialisation";
+	klninfo->clock = atoi(newvalue);
+	return NULL;
+}
+
+static const struct bfg_set_device_definition klondike_set_device_funcs[] = {
+	{"clock", klondike_set_clock, "clock frequency (can only be set at startup, with --set-device)"},
+	{NULL}
+};
+
 static
 bool klondike_lowl_match(const struct lowlevel_device_info * const info)
 {
@@ -875,12 +888,15 @@ bool klondike_lowl_probe(const struct lowlevel_device_info * const info)
 		.drv = &klondike_drv,
 		.deven = DEV_ENABLED,
 		.threads = 1,
+		.targettemp = 50,
 		.cutofftemp = (int)KLN_KILLWORK_TEMP,
+		.set_device_funcs = klondike_set_device_funcs,
 	};
 
 	klninfo = calloc(1, sizeof(*klninfo));
 	if (unlikely(!klninfo))
 		quit(1, "Failed to calloc klninfo in klondke_detect_one");
+	klninfo->clock = 282;
 	klncgpu->device_data = (void *)klninfo;
 
 	klninfo->free = new_klist_set(klncgpu);

+ 11 - 1
icarus-common.h

@@ -56,6 +56,15 @@ struct ICARUS_HISTORY {
 };
 
 enum timing_mode { MODE_DEFAULT, MODE_SHORT, MODE_LONG, MODE_VALUE };
+enum icarus_reopen_mode {
+	IRM_NEVER,
+	IRM_TIMEOUT,
+	IRM_CYCLE,
+};
+enum icarus_user_settings {
+	IUS_WORK_DIVISION = 1,
+	IUS_FPGA_COUNT    = 2,
+};
 
 struct ICARUS_INFO {
 	// time to calculate the golden_ob
@@ -90,7 +99,8 @@ struct ICARUS_INFO {
 	int work_division;
 	int fpga_count;
 	uint32_t nonce_mask;
-	int quirk_reopen;
+	enum icarus_reopen_mode reopen_mode;
+	bool reopen_now;
 	uint8_t user_set;
 	bool continue_search;
 

+ 139 - 192
miner.c

@@ -231,16 +231,8 @@ bool opt_api_network;
 bool opt_delaynet;
 bool opt_disable_pool;
 static bool no_work;
-char *opt_icarus_options = NULL;
-char *opt_icarus_timing = NULL;
 bool opt_worktime;
 bool opt_weighed_stats;
-#ifdef USE_AVALON
-char *opt_avalon_options = NULL;
-#endif
-#ifdef USE_KLONDIKE
-char *opt_klondike_options = NULL;
-#endif
 
 char *opt_kernel_path;
 char *cgminer_path;
@@ -1653,107 +1645,32 @@ static char *set_sharelog(char *arg)
 	return _bfgopt_set_file(arg, &sharelog_file, "a", "share log");
 }
 
-static char *temp_cutoff_str = "";
-static char *temp_target_str = "";
-
 char *set_temp_cutoff(char *arg)
 {
-	int val;
-
-	if (!(arg && arg[0]))
-		return "Invalid parameters for set temp cutoff";
-	val = atoi(arg);
-	if (val < 0 || val > 200)
-		return "Invalid value passed to set temp cutoff";
-	temp_cutoff_str = arg;
-
+	if (strchr(arg, ','))
+		return "temp-cutoff no longer supports comma-delimited syntax, use --set-device for better control";
+	applog(LOG_WARNING, "temp-cutoff is deprecated! Use --set-device for better control");
+	
+	char buf[0x100];
+	snprintf(buf, sizeof(buf), "all:temp-cutoff=%s", arg);
+	applog(LOG_DEBUG, "%s: Using --set-device %s", __func__, buf);
+	string_elist_add(buf, &opt_set_device_list);
+	
 	return NULL;
 }
 
 char *set_temp_target(char *arg)
 {
-	int val;
-
-	if (!(arg && arg[0]))
-		return "Invalid parameters for set temp target";
-	val = atoi(arg);
-	if (val < 0 || val > 200)
-		return "Invalid value passed to set temp target";
-	temp_target_str = arg;
-
-	return NULL;
-}
-
-// For a single element string, this always returns the number (for all calls)
-// For multi-element strings, it returns each element as a number in order, and 0 when there are no more
-static int temp_strtok(char *base, char **n)
-{
-	char *i = *n;
-	char *p = strchr(i, ',');
-	if (p) {
-		p[0] = '\0';
-		*n = &p[1];
-	}
-	else
-	if (base != i)
-		*n = strchr(i, '\0');
-	return atoi(i);
-}
-
-static void load_temp_config_cgpu(struct cgpu_info *cgpu, char **cutoff_np, char **target_np)
-{
-	int target_off, val;
-	
-	// cutoff default may be specified by driver during probe; otherwise, opt_cutofftemp (const)
-	if (!cgpu->cutofftemp)
-		cgpu->cutofftemp = opt_cutofftemp;
-	
-	// target default may be specified by driver, and is moved with offset; otherwise, offset minus 6
-	if (cgpu->targettemp)
-		target_off = cgpu->targettemp - cgpu->cutofftemp;
-	else
-		target_off = -6;
-	
-	cgpu->cutofftemp_default = cgpu->cutofftemp;
-	
-	val = temp_strtok(temp_cutoff_str, cutoff_np);
-	if (val < 0 || val > 200)
-		quit(1, "Invalid value passed to set temp cutoff");
-	if (val)
-		cgpu->cutofftemp = val;
+	if (strchr(arg, ','))
+		return "temp-target no longer supports comma-delimited syntax, use --set-device for better control";
+	applog(LOG_WARNING, "temp-target is deprecated! Use --set-device for better control");
 	
-	cgpu->targettemp_default = cgpu->cutofftemp + target_off;
-	
-	val = temp_strtok(temp_target_str, target_np);
-	if (val < 0 || val > 200)
-		quit(1, "Invalid value passed to set temp target");
-	if (val)
-		cgpu->targettemp = val;
-	else
-		cgpu->targettemp = cgpu->cutofftemp + target_off;
+	char buf[0x100];
+	snprintf(buf, sizeof(buf), "all:temp-target=%s", arg);
+	applog(LOG_DEBUG, "%s: Using --set-device %s", __func__, buf);
+	string_elist_add(buf, &opt_set_device_list);
 	
-	applog(LOG_DEBUG, "%"PRIprepr": Set temperature config: target=%d cutoff=%d",
-	       cgpu->proc_repr,
-	       cgpu->targettemp, cgpu->cutofftemp);
-}
-
-static void load_temp_config()
-{
-	int i;
-	char *cutoff_n, *target_n;
-	struct cgpu_info *cgpu;
-
-	cutoff_n = temp_cutoff_str;
-	target_n = temp_target_str;
-
-	for (i = 0; i < total_devices; ++i) {
-		cgpu = get_devices(i);
-		load_temp_config_cgpu(cgpu, &cutoff_n, &target_n);
-	}
-	if (cutoff_n != temp_cutoff_str && cutoff_n[0])
-		quit(1, "Too many values passed to set temp cutoff");
-	if (target_n != temp_target_str && target_n[0])
-		quit(1, "Too many values passed to set temp target");
+	return NULL;
 }
 
 static char *set_api_allow(const char *arg)
@@ -1785,26 +1702,88 @@ static char *set_api_mcast_des(const char *arg)
 }
 
 #ifdef USE_ICARUS
+extern const struct bfg_set_device_definition icarus_set_device_funcs[];
+
 static char *set_icarus_options(const char *arg)
 {
-	opt_set_charp(arg, &opt_icarus_options);
-
+	if (strchr(arg, ','))
+		return "icarus-options no longer supports comma-delimited syntax, see README.FPGA for better control";
+	applog(LOG_WARNING, "icarus-options is deprecated! See README.FPGA for better control");
+	
+	char *opts = strdup(arg), *argdup;
+	argdup = opts;
+	const struct bfg_set_device_definition *sdf = icarus_set_device_funcs;
+	const char *drivers[] = {"antminer", "cairnsmore", "erupter", "icarus"};
+	char buf[0x100], *saveptr, *opt;
+	for (int i = 0; i < 4; ++i, ++sdf)
+	{
+		opt = strtok_r(opts, ":", &saveptr);
+		opts = NULL;
+		
+		if (!opt)
+			break;
+		
+		if (!opt[0])
+			continue;
+		
+		for (int j = 0; j < 4; ++j)
+		{
+			snprintf(buf, sizeof(buf), "%s:%s=%s", drivers[j], sdf->optname, opt);
+			applog(LOG_DEBUG, "%s: Using --set-device %s", __func__, buf);
+			string_elist_add(buf, &opt_set_device_list);
+		}
+	}
+	free(argdup);
 	return NULL;
 }
 
 static char *set_icarus_timing(const char *arg)
 {
-	opt_set_charp(arg, &opt_icarus_timing);
-
+	if (strchr(arg, ','))
+		return "icarus-timing no longer supports comma-delimited syntax, see README.FPGA for better control";
+	applog(LOG_WARNING, "icarus-timing is deprecated! See README.FPGA for better control");
+	
+	const char *drivers[] = {"antminer", "cairnsmore", "erupter", "icarus"};
+	char buf[0x100];
+	for (int j = 0; j < 4; ++j)
+	{
+		snprintf(buf, sizeof(buf), "%s:timing=%s", drivers[j], arg);
+		applog(LOG_DEBUG, "%s: Using --set-device %s", __func__, buf);
+		string_elist_add(buf, &opt_set_device_list);
+	}
 	return NULL;
 }
 #endif
 
 #ifdef USE_AVALON
+extern const struct bfg_set_device_definition avalon_set_device_funcs[];
+
 static char *set_avalon_options(const char *arg)
 {
-	opt_set_charp(arg, &opt_avalon_options);
-
+	if (strchr(arg, ','))
+		return "avalon-options no longer supports comma-delimited syntax, see README.FPGA for better control";
+	applog(LOG_WARNING, "avalon-options is deprecated! See README.FPGA for better control");
+	
+	char *opts = strdup(arg), *argdup;
+	argdup = opts;
+	const struct bfg_set_device_definition *sdf = avalon_set_device_funcs;
+	char buf[0x100], *saveptr, *opt;
+	for (int i = 0; i < 5; ++i, ++sdf)
+	{
+		opt = strtok_r(opts, ":", &saveptr);
+		opts = NULL;
+		
+		if (!opt)
+			break;
+		
+		if (!opt[0])
+			continue;
+		
+		snprintf(buf, sizeof(buf), "avalon:%s=%s", sdf->optname, opt);
+		applog(LOG_DEBUG, "%s: Using --set-device %s", __func__, buf);
+		string_elist_add(buf, &opt_set_device_list);
+	}
+	free(argdup);
 	return NULL;
 }
 #endif
@@ -1812,8 +1791,25 @@ static char *set_avalon_options(const char *arg)
 #ifdef USE_KLONDIKE
 static char *set_klondike_options(const char *arg)
 {
-	opt_set_charp(arg, &opt_klondike_options);
-
+	char buf[0x100];
+	int hashclock;
+	double temptarget;
+	switch (sscanf(arg, "%d:%lf", &hashclock, &temptarget))
+	{
+		default:
+			return "Unrecognised --klondike-options";
+		case 2:
+			snprintf(buf, sizeof(buf), "klondike:temp-target=%lf", temptarget);
+			applog(LOG_DEBUG, "%s: Using --set-device %s", __func__, buf);
+			string_elist_add(buf, &opt_set_device_list);
+			// fallthru
+		case 1:
+			snprintf(buf, sizeof(buf), "klondike:clock=%d", hashclock);
+			applog(LOG_DEBUG, "%s: Using --set-device %s", __func__, buf);
+			string_elist_add(buf, &opt_set_device_list);
+	}
+	applog(LOG_WARNING, "klondike-options is deprecated! Use --set-device for better control");
+	
 	return NULL;
 }
 #endif
@@ -2282,7 +2278,7 @@ static struct opt_table opt_config_table[] = {
 #endif
 	OPT_WITH_ARG("--temp-cutoff",
 		     set_temp_cutoff, NULL, &opt_cutofftemp,
-		     "Maximum temperature devices will be allowed to reach before being disabled, one value or comma separated list"),
+		     opt_hidden),
 	OPT_WITH_ARG("--temp-hysteresis",
 		     set_int_1_to_10, opt_show_intval, &opt_hysteresis,
 		     "Set how much the temperature can fluctuate outside limits when automanaging speeds"),
@@ -2293,7 +2289,7 @@ static struct opt_table opt_config_table[] = {
 #endif
 	OPT_WITH_ARG("--temp-target",
 		     set_temp_target, NULL, NULL,
-		     "Target temperature when automatically managing fan and clock speeds, one value or comma separated list"),
+		     opt_hidden),
 	OPT_WITHOUT_ARG("--text-only|-T",
 			opt_set_invbool, &use_curses,
 #ifdef HAVE_CURSES
@@ -6361,57 +6357,6 @@ char *json_escape(const char *str)
 	return buf;
 }
 
-void _write_config_temps(FILE *fcfg, const char *configname, size_t settingoffset, size_t defoffset)
-{
-	int i, commas;
-	int *setp, allset;
-	uint8_t *defp;
-	
-	for (i = 0; ; ++i)
-	{
-		if (i == total_devices)
-			// All defaults
-			return;
-		setp = ((void*)devices[i]) + settingoffset;
-		defp = ((void*)devices[i]) + defoffset;
-		allset = *setp;
-		if (*setp != *defp)
-			break;
-	}
-	
-	fprintf(fcfg, ",\n\"%s\" : \"", configname);
-	
-	for (i = 1; ; ++i)
-	{
-		if (i == total_devices)
-		{
-			// All the same
-			fprintf(fcfg, "%d\"", allset);
-			return;
-		}
-		setp = ((void*)devices[i]) + settingoffset;
-		if (allset != *setp)
-			break;
-	}
-	
-	commas = 0;
-	for (i = 0; i < total_devices; ++i)
-	{
-		setp = ((void*)devices[i]) + settingoffset;
-		defp = ((void*)devices[i]) + defoffset;
-		if (*setp != *defp)
-		{
-			for ( ; commas; --commas)
-				fputs(",", fcfg);
-			fprintf(fcfg, "%d", *setp);
-		}
-		++commas;
-	}
-	fputs("\"", fcfg);
-}
-#define write_config_temps(fcfg, configname, settingname)  \
-	_write_config_temps(fcfg, configname, offsetof(struct cgpu_info, settingname), offsetof(struct cgpu_info, settingname ## _default))
-
 static
 void _write_config_string_elist(FILE *fcfg, const char *configname, struct string_elist * const elist)
 {
@@ -6458,8 +6403,6 @@ void write_config(FILE *fcfg)
 	}
 	fputs("\n]\n", fcfg);
 
-	write_config_temps(fcfg, "temp-cutoff", cutofftemp);
-	write_config_temps(fcfg, "temp-target", targettemp);
 #ifdef HAVE_OPENCL
 	if (nDevs) {
 		/* Write GPU device values */
@@ -6634,14 +6577,6 @@ void write_config(FILE *fcfg)
 		fprintf(fcfg, ",\n\"api-description\" : \"%s\"", json_escape(opt_api_description));
 	if (opt_api_groups)
 		fprintf(fcfg, ",\n\"api-groups\" : \"%s\"", json_escape(opt_api_groups));
-	if (opt_icarus_options)
-		fprintf(fcfg, ",\n\"icarus-options\" : \"%s\"", json_escape(opt_icarus_options));
-	if (opt_icarus_timing)
-		fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", json_escape(opt_icarus_timing));
-#ifdef USE_KLONDIKE
-	if (opt_klondike_options)
-		fprintf(fcfg, ",\n\"klondike-options\" : \"%s\"", json_escape(opt_klondike_options));
-#endif
 	fputs("\n}\n", fcfg);
 
 	json_escape_free();
@@ -9733,7 +9668,6 @@ void proc_enable(struct cgpu_info *cgpu)
 
 void cgpu_set_defaults(struct cgpu_info * const cgpu)
 {
-	const struct device_drv * const drv = cgpu->drv;
 	struct string_elist *setstr_elist;
 	const char *p, *p2;
 	char replybuf[0x2000];
@@ -9757,13 +9691,6 @@ void cgpu_set_defaults(struct cgpu_info * const cgpu)
 		applog(LOG_DEBUG, "%"PRIpreprv": %s: Matched with set default: %s",
 		       cgpu->proc_repr, __func__, setstr);
 		
-		if (!drv->set_device)
-		{
-			applog(LOG_WARNING, "%"PRIpreprv": set_device is not implemented (trying to apply rule: %s)",
-			       cgpu->proc_repr, setstr);
-			continue;
-		}
-		
 		if (p[0] == ':')
 			++p;
 		p2 = strchr(p, '=');
@@ -9788,18 +9715,31 @@ void cgpu_set_defaults(struct cgpu_info * const cgpu)
 			memcpy(setval, p2, L);
 		setval[L] = '\0';
 		
-		p = drv->set_device(cgpu, opt, setval, replybuf);
-		if (p)
-			applog(LOG_WARNING, "%"PRIpreprv": Applying rule %s: %s",
-			       cgpu->proc_repr, setstr, p);
-		else
-			applog(LOG_DEBUG, "%"PRIpreprv": Applied rule %s",
-			       cgpu->proc_repr, setstr);
+		enum bfg_set_device_replytype success;
+		p = proc_set_device(cgpu, opt, setval, replybuf, &success);
+		switch (success)
+		{
+			case SDR_OK:
+				applog(LOG_DEBUG, "%"PRIpreprv": Applied rule %s%s%s",
+				       cgpu->proc_repr, setstr,
+				       p ? ": " : "", p ?: "");
+				break;
+			case SDR_ERR:
+			case SDR_HELP:
+			case SDR_UNKNOWN:
+				applog(LOG_WARNING, "%"PRIpreprv": Applying rule %s: %s",
+				       cgpu->proc_repr, setstr, p);
+				break;
+			case SDR_AUTO:
+			case SDR_NOSUPP:
+				applog(LOG_WARNING, "%"PRIpreprv": set_device is not implemented (trying to apply rule: %s)",
+				       cgpu->proc_repr, setstr);
+		}
 	}
 	cgpu->already_set_defaults = true;
 }
 
-void drv_set_defaults(const struct device_drv * const drv, char *(*set_func)(struct cgpu_info *, char *, char *, char *), void *userp)
+void drv_set_defaults(const struct device_drv * const drv, const void *datap, void *userp, const char * const devpath, const char * const serial, const int mode)
 {
 	struct device_drv dummy_drv = *drv;
 	struct cgpu_info dummy_cgpu = {
@@ -9808,9 +9748,20 @@ void drv_set_defaults(const struct device_drv * const drv, char *(*set_func)(str
 		.device_id = -1,
 		.proc_id = -1,
 		.device_data = userp,
+		.device_path = devpath,
+		.dev_serial = serial,
 	};
 	strcpy(dummy_cgpu.proc_repr, drv->name);
-	dummy_drv.set_device = set_func;
+	switch (mode)
+	{
+		case 0:
+			dummy_drv.set_device = datap;
+			break;
+		case 1:
+			dummy_drv.set_device = NULL;
+			dummy_cgpu.set_device_funcs = datap;
+			break;
+	}
 	cgpu_set_defaults(&dummy_cgpu);
 }
 
@@ -10919,7 +10870,6 @@ int create_new_cgpus(void (*addfunc)(void*), void *arg)
 	struct cgpu_info *cgpu;
 	struct thr_info *thr;
 	void *p;
-	char *dummy = "\0";
 	
 	mutex_lock(&mutex);
 	devcount = total_devices;
@@ -10973,7 +10923,6 @@ int create_new_cgpus(void (*addfunc)(void*), void *arg)
 	{
 		cgpu = devices_new[i];
 		
-		load_temp_config_cgpu(cgpu, &dummy, &dummy);
 		allocate_cgpu(cgpu, &k);
 	}
 	for (i = 0; i < total_devices_new; ++i)
@@ -11606,8 +11555,6 @@ int main(int argc, char *argv[])
 			applog(LOG_WARNING, "Waiting for devices");
 	}
 
-	load_temp_config();
-
 #ifdef HAVE_CURSES
 	switch_logsize();
 #endif

+ 3 - 3
miner.h

@@ -446,6 +446,7 @@ struct cgpu_info {
 	int cgminer_id;
 	int device_line_id;
 	struct device_drv *drv;
+	const struct bfg_set_device_definition *set_device_funcs;
 	const char *devtype;
 	int device_id;
 	char *dev_repr;
@@ -538,9 +539,8 @@ struct cgpu_info {
 
 	float temp;
 	int cutofftemp;
-	uint8_t cutofftemp_default;
 	int targettemp;
-	uint8_t targettemp_default;
+	bool targettemp_user;
 
 #ifdef HAVE_ADL
 	bool has_adl;
@@ -1045,7 +1045,7 @@ extern void proc_enable(struct cgpu_info *);
 extern void reinit_device(struct cgpu_info *cgpu);
 
 extern void cgpu_set_defaults(struct cgpu_info *);
-extern void drv_set_defaults(const struct device_drv *, char *(*set_func)(struct cgpu_info *, char *, char *, char *), void *userp);
+extern void drv_set_defaults(const struct device_drv *, const void *, void *userp, const char *devpath, const char *serial, int mode);
 
 #ifdef HAVE_ADL
 extern bool gpu_stats(int gpu, float *temp, int *engineclock, int *memclock, float *vddc, int *activity, int *fanspeed, int *fanpercent, int *powertune);