Commit 34b15a6d469a3f7b1c7db6f8e7b6dd55d922322a

Con Kolivas 2012-12-18T19:36:41

Merge pull request #370 from kanoi/mmq API V1.23 - new pgaset command, to be used soon + MMQ add api pgaset for clock + ensure delta clock can never exceed limits

diff --git a/API-README b/API-README
index 06fec76..b205c90 100644
--- a/API-README
+++ b/API-README
@@ -330,9 +330,23 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               queue, scantime, expiry
                               N is an integer in the range 0 to 9999
 
- substats      USBSTATS       Stats of all LIBUSB mining devices except ztex
+ usbstats      USBSTATS       Stats of all LIBUSB mining devices except ztex
                               e.g. Name=MMQ,ID=0,Stat=SendWork,Count=99,...|
 
+ pgaset|N,opt[,val] (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of setting PGA N with opt[,val]
+                              This is only available if PGA mining is enabled
+
+                              If the PGA does not support any set options, it will
+                              always return a WARN stating pgaset isn't supported
+
+                              If opt=help it will return an INFO status with a
+                              help message about the options available
+
+                              The current options are:
+                               MMQ opt=clock val=160 to 230 (and a multiple of 2)
+
 When you enable, disable or restart a GPU or PGA, you will also get Thread messages
 in the cgminer status window
 
@@ -386,7 +400,14 @@ miner.php - an example web page to access the API
 Feature Changelog for external applications using the API:
 
 
-API V1.22
+API V1.23
+
+Added API commands:
+ 'pgaset' - with: MMQ opt=clock val=160 to 230 (and a multiple of 2)
+
+----------
+
+API V1.22 (cgminer v2.10.1)
 
 Enforced output limitation:
  all extra records beyond the output limit of the API (~64k) are ignored
diff --git a/api.c b/api.c
index 4cec476..659d934 100644
--- a/api.c
+++ b/api.c
@@ -133,7 +133,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 
-static const char *APIVERSION = "1.22";
+static const char *APIVERSION = "1.23";
 static const char *DEAD = "Dead";
 #if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
 static const char *SICK = "Sick";
@@ -379,6 +379,14 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_USBSTA 87
 #define MSG_NOUSTA 88
 
+#ifdef HAVE_AN_FPGA
+#define MSG_MISPGAOPT 89
+#define MSG_PGANOSET 90
+#define MSG_PGAHELP 91
+#define MSG_PGASETOK 92
+#define MSG_PGASETERR 93
+#endif
+
 enum code_severity {
 	SEVERITY_ERR,
 	SEVERITY_WARN,
@@ -544,6 +552,13 @@ struct CODES {
  { SEVERITY_ERR,   MSG_CONVAL,	PARAM_STR,	"Missing config value N for '%s,N'" },
  { SEVERITY_SUCC,  MSG_USBSTA,	PARAM_NONE,	"USB Statistics" },
  { SEVERITY_INFO,  MSG_NOUSTA,	PARAM_NONE,	"No USB Statistics" },
+#ifdef HAVE_AN_FPGA
+ { SEVERITY_ERR,   MSG_MISPGAOPT, PARAM_NONE,	"Missing option after PGA number" },
+ { 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_BOTH,	"PGA %d set OK" },
+ { SEVERITY_ERR,   MSG_PGASETERR, PARAM_BOTH,	"PGA %d set failed: %s" },
+#endif
  { SEVERITY_FAIL, 0, 0, NULL }
 };
 
@@ -3142,6 +3157,64 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may
 #endif
 }
 
+#ifdef HAVE_AN_FPGA
+static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	char buf[TMPBUFSIZ];
+	int numpga = numpgas();
+
+	if (numpga == 0) {
+		message(io_data, MSG_PGANON, 0, NULL, isjson);
+		return;
+	}
+
+	if (param == NULL || *param == '\0') {
+		message(io_data, MSG_MISID, 0, NULL, isjson);
+		return;
+	}
+
+	char *opt = strchr(param, ',');
+	if (opt)
+		*(opt++) = '\0';
+	if (!opt || !*opt) {
+		message(io_data, MSG_MISPGAOPT, 0, NULL, isjson);
+		return;
+	}
+
+	int id = atoi(param);
+	if (id < 0 || id >= numpga) {
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	int dev = pgadevice(id);
+	if (dev < 0) { // Should never happen
+		message(io_data, MSG_INVPGA, id, NULL, isjson);
+		return;
+	}
+
+	struct cgpu_info *cgpu = devices[dev];
+	struct device_api *api = cgpu->api;
+
+	char *set = strchr(opt, ',');
+	if (set)
+		*(set++) = '\0';
+
+	if (!api->set_device)
+		message(io_data, MSG_PGANOSET, id, NULL, isjson);
+	else {
+		char *ret = api->set_device(cgpu, opt, set, buf);
+		if (ret) {
+			if (strcasecmp(opt, "help") == 0)
+				message(io_data, MSG_PGAHELP, id, ret, isjson);
+			else
+				message(io_data, MSG_PGASETERR, id, ret, isjson);
+		} else
+			message(io_data, MSG_PGASETOK, id, NULL, isjson);
+	}
+}
+#endif
+
 static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group);
 
 struct CMDS {
@@ -3198,6 +3271,9 @@ struct CMDS {
 	{ "debug",		debugstate,	true },
 	{ "setconfig",		setconfig,	true },
 	{ "usbstats",		usbstats,	false },
+#ifdef HAVE_AN_FPGA
+	{ "pgaset",		pgaset,		true },
+#endif
 	{ NULL,			NULL,		false }
 };
 
diff --git a/driver-modminer.c b/driver-modminer.c
index 832b854..4612a1c 100644
--- a/driver-modminer.c
+++ b/driver-modminer.c
@@ -599,7 +599,13 @@ static bool modminer_fpga_prepare(struct thr_info *thr)
  *
  * N.B. clock must always be a multiple of 2
  */
-static bool modminer_delta_clock(struct thr_info *thr, int delta, bool temp)
+static const char *clockoldwork = "clock already changed for this work";
+static const char *clocktoolow = "clock too low";
+static const char *clocktoohi = "clock too high";
+static const char *clocksetfail = "clock set command failed";
+static const char *clockreplyfail = "clock reply failed";
+
+static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool temp, bool force)
 {
 	struct cgpu_info *modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
@@ -607,8 +613,8 @@ static bool modminer_delta_clock(struct thr_info *thr, int delta, bool temp)
 	int err, amount;
 
 	// Only do once if multiple shares per work or multiple reasons
-	if (!state->new_work)
-		return false;
+	if (!state->new_work && !force)
+		return clockoldwork;
 
 	state->new_work = false;
 
@@ -617,11 +623,11 @@ static bool modminer_delta_clock(struct thr_info *thr, int delta, bool temp)
 	state->hw_errors = 0;
 
 	// FYI clock drop has little effect on temp
-	if (delta < 0 && modminer->clock <= MODMINER_MIN_CLOCK)
-		return false;
+	if (delta < 0 && (modminer->clock + delta) < MODMINER_MIN_CLOCK)
+		return clocktoolow;
 
-	if (delta > 0 && modminer->clock >= MODMINER_MAX_CLOCK)
-		return false;
+	if (delta > 0 && (modminer->clock + delta) > MODMINER_MAX_CLOCK)
+		return clocktoohi;
 
 	if (delta < 0) {
 		if (temp)
@@ -649,7 +655,7 @@ static bool modminer_delta_clock(struct thr_info *thr, int delta, bool temp)
 		applog(LOG_ERR, "%s%u: Error writing set clock speed (%d:%d)",
 			modminer->api->name, modminer->device_id, amount, err);
 
-		return false;
+		return clocksetfail;
 	}
 
 	if ((err = usb_read(modminer, (char *)(&buf), 1, &amount, C_REPLYSETCLOCK)) < 0 || amount != 1) {
@@ -658,7 +664,7 @@ static bool modminer_delta_clock(struct thr_info *thr, int delta, bool temp)
 		applog(LOG_ERR, "%s%u: Error reading set clock speed (%d:%d)",
 			modminer->api->name, modminer->device_id, amount, err);
 
-		return false;
+		return clockreplyfail;
 	}
 
 	mutex_unlock(modminer->modminer_mutex);
@@ -668,7 +674,7 @@ static bool modminer_delta_clock(struct thr_info *thr, int delta, bool temp)
 			(delta < 0) ? "down " : (delta > 0 ? "up " : ""),
 			modminer->clock);
 
-	return true;
+	return NULL;
 }
 
 static bool modminer_fpga_init(struct thr_info *thr)
@@ -715,7 +721,7 @@ static bool modminer_fpga_init(struct thr_info *thr)
 	}
 
 	modminer->clock = MODMINER_DEF_CLOCK;
-	modminer_delta_clock(thr, MODMINER_CLOCK_SET, false);
+	modminer_delta_clock(thr, MODMINER_CLOCK_SET, false, false);
 
 	thr->primary_thread = true;
 
@@ -831,7 +837,7 @@ static void check_temperature(struct thr_info *thr)
 					modminer->api->name, modminer->device_id,
 					MODMINER_CUTOFF_TEMP, modminer->temp);
 
-				modminer_delta_clock(thr, MODMINER_CLOCK_CUTOFF, true);
+				modminer_delta_clock(thr, MODMINER_CLOCK_CUTOFF, true, false);
 				state->overheated = true;
 				dev_error(modminer, REASON_DEV_THERMAL_CUTOFF);
 			} else {
@@ -841,7 +847,7 @@ static void check_temperature(struct thr_info *thr)
 
 				// If it's defined to be 0 then don't call modminer_delta_clock()
 				if (MODMINER_CLOCK_OVERHEAT != 0)
-					modminer_delta_clock(thr, MODMINER_CLOCK_OVERHEAT, true);
+					modminer_delta_clock(thr, MODMINER_CLOCK_OVERHEAT, true, false);
 				state->overheated = true;
 				dev_error(modminer, REASON_DEV_OVER_HEAT);
 			}
@@ -950,7 +956,7 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 					if (modminer->clock > MODMINER_DEF_CLOCK || state->hw_errors > 1) {
 						float pct = (state->hw_errors * 100.0 / (state->shares ? : 1.0));
 						if (pct >= MODMINER_HW_ERROR_PERCENT)
-							modminer_delta_clock(thr, MODMINER_CLOCK_DOWN, false);
+							modminer_delta_clock(thr, MODMINER_CLOCK_DOWN, false, false);
 					}
 				}
 			} else {
@@ -959,7 +965,7 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 				// If we've reached the required good shares in a row then clock up
 				if (((state->shares - state->shares_last_hw) >= state->shares_to_good) &&
 						modminer->temp < MODMINER_TEMP_UP_LIMIT)
-					modminer_delta_clock(thr, MODMINER_CLOCK_UP, false);
+					modminer_delta_clock(thr, MODMINER_CLOCK_UP, false, false);
 			}
 		} else {
 			// on rare occasions - the MMQ can just stop returning valid nonces
@@ -967,7 +973,7 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 			gettimeofday(&now, NULL);
 			if (tdiff(&now, &state->last_nonce) >= death) {
 				if (state->death_stage_one) {
-					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false);
+					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true);
 					applog(LOG_ERR, "%s%u: DEATH clock down",
 						modminer->api->name, modminer->device_id);
 
@@ -977,7 +983,7 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 					state->death_stage_one = false;
 					return -1;
 				} else {
-					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false);
+					modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true);
 					applog(LOG_ERR, "%s%u: death clock down",
 						modminer->api->name, modminer->device_id);
 
@@ -1097,11 +1103,50 @@ static void modminer_fpga_shutdown(struct thr_info *thr)
 	free(thr->cgpu_data);
 }
 
+static char *modminer_set_device(struct cgpu_info *modminer, char *option, char *setting, char *replybuf)
+{
+	const char *ret;
+	int val;
+
+	if (strcasecmp(option, "help") == 0) {
+		sprintf(replybuf, "clock: range %d-%d and a multiple of 2",
+					MODMINER_MIN_CLOCK, MODMINER_MAX_CLOCK);
+		return replybuf;
+	}
+
+	if (strcasecmp(option, "clock") == 0) {
+		if (!setting || !*setting) {
+			sprintf(replybuf, "missing clock setting");
+			return replybuf;
+		}
+
+		val = atoi(setting);
+		if (val < MODMINER_MIN_CLOCK || val > MODMINER_MAX_CLOCK || (val & 1) != 0) {
+			sprintf(replybuf, "invalid clock: '%s' valid range %d-%d and a multiple of 2",
+						setting, MODMINER_MIN_CLOCK, MODMINER_MAX_CLOCK);
+			return replybuf;
+		}
+
+		val -= (int)(modminer->clock);
+
+		ret = modminer_delta_clock(modminer->thr[0], val, false, true);
+		if (ret) {
+			sprintf(replybuf, "Set clock failed: %s", ret);
+			return replybuf;
+		} else
+			return NULL;
+	}
+
+	sprintf(replybuf, "Unknown option: %s", option);
+	return replybuf;
+}
+
 struct device_api modminer_api = {
 	.dname = "modminer",
 	.name = "MMQ",
 	.api_detect = modminer_detect,
 	.get_statline_before = get_modminer_statline_before,
+	.set_device = modminer_set_device,
 	.thread_prepare = modminer_fpga_prepare,
 	.thread_init = modminer_fpga_init,
 	.scanhash = modminer_scanhash,
diff --git a/miner.h b/miner.h
index b0659d9..2b5897a 100644
--- a/miner.h
+++ b/miner.h
@@ -277,6 +277,7 @@ struct device_api {
 	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
+	char *(*set_device)(struct cgpu_info *, char *option, char *setting, char *replybuf);
 
 	// Thread-specific functions
 	bool (*thread_prepare)(struct thr_info *);