From e931b72753233ba25a84015b2c154c3700457972 Mon Sep 17 00:00:00 2001 From: Kano Date: Wed, 19 Dec 2012 10:43:27 +1100 Subject: [PATCH 1/4] API V1.23 - new pgaset command, to be used soon --- API-README | 22 +++++++++++++-- api.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++- miner.h | 1 + 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/API-README b/API-README index 1a686f52..1bd5fc69 100644 --- a/API-README +++ b/API-README @@ -330,9 +330,20 @@ 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 + When you enable, disable or restart a GPU or PGA, you will also get Thread messages in the cgminer status window @@ -386,7 +397,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' + +---------- + +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 4cec4767..659d934a 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/miner.h b/miner.h index b0659d9e..2b5897a9 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 *); From 4ab19c7de6658675b9e7ab89c517c25582a4161f Mon Sep 17 00:00:00 2001 From: Kano Date: Wed, 19 Dec 2012 10:56:00 +1100 Subject: [PATCH 2/4] MMQ add api pgaset for clock --- API-README | 5 +++- driver-modminer.c | 75 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/API-README b/API-README index 1bd5fc69..6a13a451 100644 --- a/API-README +++ b/API-README @@ -344,6 +344,9 @@ The list of requests - a (*) means it requires privileged access - and replies a 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 @@ -400,7 +403,7 @@ Feature Changelog for external applications using the API: API V1.23 Added API commands: - 'pgaset' + 'pgaset' - with: MMQ opt=clock val=160 to 230 (and a multiple of 2) ---------- diff --git a/driver-modminer.c b/driver-modminer.c index 832b854b..702da0cf 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; @@ -618,10 +624,10 @@ static bool modminer_delta_clock(struct thr_info *thr, int delta, bool temp) // FYI clock drop has little effect on temp if (delta < 0 && modminer->clock <= MODMINER_MIN_CLOCK) - return false; + return CLOCKTOOLOW; if (delta > 0 && modminer->clock >= MODMINER_MAX_CLOCK) - return false; + 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, From d848289e8c3d0649c93d3abf3ca053807aaea7a1 Mon Sep 17 00:00:00 2001 From: Kano Date: Wed, 19 Dec 2012 14:25:58 +1100 Subject: [PATCH 3/4] MMQ lowercase new string constants --- driver-modminer.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/driver-modminer.c b/driver-modminer.c index 702da0cf..07f4400b 100644 --- a/driver-modminer.c +++ b/driver-modminer.c @@ -599,11 +599,11 @@ static bool modminer_fpga_prepare(struct thr_info *thr) * * N.B. clock must always be a multiple of 2 */ -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 *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) { @@ -614,7 +614,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te // Only do once if multiple shares per work or multiple reasons if (!state->new_work && !force) - return CLOCKOLDWORK; + return clockoldwork; state->new_work = false; @@ -624,10 +624,10 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te // FYI clock drop has little effect on temp if (delta < 0 && modminer->clock <= MODMINER_MIN_CLOCK) - return CLOCKTOOLOW; + return clocktoolow; if (delta > 0 && modminer->clock >= MODMINER_MAX_CLOCK) - return CLOCKTOOHI; + return clocktoohi; if (delta < 0) { if (temp) @@ -655,7 +655,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te applog(LOG_ERR, "%s%u: Error writing set clock speed (%d:%d)", modminer->api->name, modminer->device_id, amount, err); - return CLOCKSETFAIL; + return clocksetfail; } if ((err = usb_read(modminer, (char *)(&buf), 1, &amount, C_REPLYSETCLOCK)) < 0 || amount != 1) { @@ -664,7 +664,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te applog(LOG_ERR, "%s%u: Error reading set clock speed (%d:%d)", modminer->api->name, modminer->device_id, amount, err); - return CLOCKREPLYFAIL; + return clockreplyfail; } mutex_unlock(modminer->modminer_mutex); From f0003055a4fac9cb9ea27ab7eb8a1ee8e30c10d3 Mon Sep 17 00:00:00 2001 From: Kano Date: Wed, 19 Dec 2012 14:30:48 +1100 Subject: [PATCH 4/4] MMQ ensure delta clock can never exceed limits --- driver-modminer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/driver-modminer.c b/driver-modminer.c index 07f4400b..4612a1c2 100644 --- a/driver-modminer.c +++ b/driver-modminer.c @@ -623,10 +623,10 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te state->hw_errors = 0; // FYI clock drop has little effect on temp - if (delta < 0 && modminer->clock <= MODMINER_MIN_CLOCK) + if (delta < 0 && (modminer->clock + delta) < MODMINER_MIN_CLOCK) return clocktoolow; - if (delta > 0 && modminer->clock >= MODMINER_MAX_CLOCK) + if (delta > 0 && (modminer->clock + delta) > MODMINER_MAX_CLOCK) return clocktoohi; if (delta < 0) {