diff --git a/Makefile.am b/Makefile.am index 02ee6f06..1a98780c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,11 +9,12 @@ endif EXTRA_DIST = example.conf m4/gnulib-cache.m4 linux-usb-cgminer \ ADL_SDK/readme.txt api-example.php miner.php \ - API.class API.java api-example.c windows-build.txt + API.class API.java api-example.c windows-build.txt \ + bitstreams/* SUBDIRS = lib compat ccan -INCLUDES = $(PTHREAD_FLAGS) -fno-strict-aliasing $(JANSSON_INCLUDES) +INCLUDES = $(PTHREAD_FLAGS) -fno-strict-aliasing $(JANSSON_INCLUDES) $(USB_FLAGS) bin_PROGRAMS = cgminer @@ -22,7 +23,7 @@ bin_SCRIPTS = *.cl cgminer_LDFLAGS = $(PTHREAD_FLAGS) cgminer_LDADD = $(DLOPEN_FLAGS) @LIBCURL_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ \ @OPENCL_LIBS@ @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \ - @UDEV_LIBS@ \ + @UDEV_LIBS@ @USB_LIBS@ \ @MATH_LIBS@ lib/libgnu.a ccan/libccan.a cgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @OPENCL_FLAGS@ @@ -37,7 +38,7 @@ cgminer_SOURCES += logging.c # GPU sources, TODO: make them selectable # the GPU portion extracted from original main.c -cgminer_SOURCES += device-gpu.h device-gpu.c +cgminer_SOURCES += driver-opencl.h driver-opencl.c # the original GPU related sources, unchanged cgminer_SOURCES += ocl.c ocl.h findnonce.c findnonce.h @@ -53,7 +54,7 @@ cgminer_SOURCES += \ sha256_altivec_4way.c # the CPU portion extracted from original main.c -cgminer_SOURCES += device-cpu.h device-cpu.c +cgminer_SOURCES += driver-cpu.h driver-cpu.c if HAS_YASM AM_CFLAGS = -DHAS_YASM @@ -68,9 +69,15 @@ endif # HAS_YASM endif # HAS_CPUMINE if HAS_BITFORCE -cgminer_SOURCES += bitforce.c +cgminer_SOURCES += driver-bitforce.c endif if HAS_ICARUS -cgminer_SOURCES += icarus.c +cgminer_SOURCES += driver-icarus.c +endif + +if HAS_ZTEX +cgminer_SOURCES += driver-ztex.c libztex.c +bitstreamsdir = $(bindir)/bitstreams +dist_bitstreams_DATA = bitstreams/* endif diff --git a/NEWS b/NEWS index 62232381..448a4257 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,27 @@ +Version 2.3.3 - April 15, 2012 + +- Don't even display that cpumining is disabled on ./configure to discourage +people from enabling it. +- Do a complete cgminer restart if the ATI Display Library fails, as it does on +windows after running for some time, when fanspeed reporting fails. +- Cache the initial arguments passed to cgminer and implement an attempted +restart option from the settings menu. +- Disable per-device status lines when there are more than 8 devices since +screen output will be corrupted, enumerating them to the log output instead at +startup. +- Reuse Vals[] array more than W[] till they're re-initialised on the second +sha256 cycle in poclbm kernel. +- Minor variable alignment in poclbm kernel. +- Make sure to disable devices with any status not being DEV_ENABLED to ensure +that thermal cutoff code works as it was setting the status to DEV_RECOVER. +- Re-initialising ADL simply made the driver fail since it is corruption over +time within the windows driver that's responsible. Revert "Attempt to +re-initialise ADL should a device that previously reported fanspeed stops +reporting it." +- Microoptimise poclbm kernel by ordering Val variables according to usage +frequency. + + Version 2.3.2 - March 31, 2012 - Damping small changes in hashrate so dramatically has the tendency to always diff --git a/README b/README index 0cb57209..63448466 100644 --- a/README +++ b/README @@ -596,6 +596,15 @@ An example request in both formats to set GPU 0 fan to 80%: The format of each reply (unless stated otherwise) is a STATUS section followed by an optional detail section +From API verion 1.7 onwards, reply strings in JSON and Text have the +necessary escaping as required to avoid ambiguity - they didn't before 1.7 +For JSON the 2 characters '"' and '\' are escaped with a '\' before them +For Text the 4 characters '|' ',' '=' and '\' are escaped the same way + +Only user entered information will contain characters that require being +escaped, such as Pool URL, User and Password or the Config save filename, +when they are returned in messages or as their values by the API + For API version 1.4 and later: The STATUS section is: @@ -622,7 +631,7 @@ The STATUS section is: This defaults to the cgminer version but is the value of --api-description if it was specified at runtime. -For API version 1.6: +For API version 1.7: The list of requests - a (*) means it requires privileged access - and replies are: @@ -700,6 +709,12 @@ The list of requests - a (*) means it requires privileged access - and replies a stating the results of disabling pool N The Msg includes the pool URL + removepool|N (*) + none There is no reply section just the STATUS section + stating the results of removing pool N + The Msg includes the pool URL + N.B. all details for the pool will be lost + gpuenable|N (*) none There is no reply section just the STATUS section stating the results of the enable request @@ -794,9 +809,8 @@ api-example.c - a 'C' program to access the API (with source code) miner.php - an example web page to access the API This includes buttons and inputs to attempt access to the privileged commands - You must modify the 2 lines near the top to change where it looks for cgminer - $miner = '127.0.0.1'; # hostname or IP address - $port = 4028; + Read the top of the file (miner.php) for details of how to tune the display + and also to use the option to display a multi-rig summary --- @@ -873,21 +887,14 @@ any further. Q: Can you change the autofan/autogpu to change speeds in a different manner? A: The defaults are sane and safe. I'm not interested in changing them -further. The starting fan speed is set to 85% in auto-fan mode as a safety -precaution, but if a specific fan speed has been set, it will use that first -before adjusting automatically. - -Q: The fanspeed starts at 85% with --auto-fan. Can I set it lower? -A: The initial fanspeed will always start at 85% unless you choose your own -value with --gpu-fan. In this case it will use the value you give it with ---gpu-fan as the first fanspeed, but it will also use this as the maximum fan -speed unless overheat is detected. +further. The starting fan speed is set to 50% in auto-fan mode as a safety +precaution. Q: Why is my efficiency above/below 100%? A: Efficiency simply means how many shares you return for the amount of work you request. It does not correlate with efficient use of your hardware, and is a measure of a combination of hardware speed, block luck, pool design and other -factors. +factors Q: What are the best parameters to pass for X pool/hardware/device. A: Virtually always, the DEFAULT parameters give the best results. Most user @@ -912,7 +919,7 @@ this time. Q: Which ATI SDK is the best for cgminer? A: At the moment, versions 2.4 and 2.5 work the best. If you are forced to use -the 2.6 SDK, -v 1 might help, along with not decreasing your memory clock speed. +the 2.6 SDK. Q: I have multiple SDKs installed, can I choose which one it uses? A: Run cgminer with the -n option and it will list all the platforms currently diff --git a/adl.c b/adl.c index 04e48684..c79e2eca 100644 --- a/adl.c +++ b/adl.c @@ -352,6 +352,9 @@ void init_adl(int nDevs) } applog(LOG_INFO, "GPU %d %s hardware monitoring enabled", gpu, lpInfo[i].strAdapterName); + if (gpus[gpu].name) + free(gpus[gpu].name); + gpus[gpu].name = lpInfo[i].strAdapterName; gpus[gpu].has_adl = true; /* Flag adl as active if any card is successfully activated */ adl_active = true; @@ -639,8 +642,6 @@ static inline int __gpu_fanspeed(struct gpu_adl *ga) return ga->lpFanSpeedValue.iFanSpeed; } -static void reinit_adl(void); - int gpu_fanspeed(int gpu) { struct gpu_adl *ga; @@ -669,8 +670,6 @@ static int __gpu_fanpercent(struct gpu_adl *ga) return ga->lpFanSpeedValue.iFanSpeed; } - - int gpu_fanpercent(int gpu) { struct gpu_adl *ga; @@ -684,9 +683,14 @@ int gpu_fanpercent(int gpu) ret = __gpu_fanpercent(ga); unlock_adl(); if (unlikely(ga->has_fanspeed && ret == -1)) { - applog(LOG_WARNING, "GPU %d stopped reporting fanspeed", gpu); - applog(LOG_WARNING, "Will attempt to re-initialise ADL"); - reinit_adl(); + applog(LOG_WARNING, "GPU %d stopped reporting fanspeed due to driver corruption", gpu); + if (opt_restart) { + applog(LOG_WARNING, "Restart enabled, will restart cgminer"); + applog(LOG_WARNING, "You can disable this with the --no-restart option"); + app_restart(); + } + applog(LOG_WARNING, "Disabling fanspeed monitoring on this device"); + ga->has_fanspeed = false; } return ret; } @@ -1368,7 +1372,7 @@ void clear_adl(int nDevs) free_adl(); } -static void reinit_adl(void) +void reinit_adl(void) { bool ret; lock_adl(); @@ -1377,8 +1381,7 @@ static void reinit_adl(void) if (!ret) { adl_active = false; applog(LOG_WARNING, "Attempt to re-initialise ADL has failed, disabling"); - } else - applog(LOG_WARNING, "ADL re-initialisation complete"); + } unlock_adl(); } #endif /* HAVE_ADL */ diff --git a/adl.h b/adl.h index f48c4206..363741c0 100644 --- a/adl.h +++ b/adl.h @@ -19,10 +19,12 @@ bool gpu_stats(int gpu, float *temp, int *engineclock, int *memclock, float *vdd void change_gpusettings(int gpu); void gpu_autotune(int gpu, enum dev_enable *denable); void clear_adl(int nDevs); +void reinit_adl(void); #else /* HAVE_ADL */ #define adl_active (0) static inline void init_adl(int nDevs) {} static inline void change_gpusettings(int gpu) { } static inline void clear_adl(int nDevs) {} +static inline void reinit_adl(void) {} #endif #endif diff --git a/api.c b/api.c index 953b1938..77ab5c24 100644 --- a/api.c +++ b/api.c @@ -25,7 +25,7 @@ #include "compat.h" #include "miner.h" -#include "device-cpu.h" /* for algo_names[], TODO: re-factor dependency */ +#include "driver-cpu.h" /* for algo_names[], TODO: re-factor dependency */ #if defined(unix) || defined(__APPLE__) #include @@ -157,7 +157,7 @@ static const char *COMMA = ","; static const char SEPARATOR = '|'; static const char GPUSEP = ','; -static const char *APIVERSION = "1.6"; +static const char *APIVERSION = "1.7"; static const char *DEAD = "Dead"; static const char *SICK = "Sick"; static const char *NOSTART = "NoStart"; @@ -337,6 +337,10 @@ static const char *JSON_PARAMETER = "parameter"; #define MSG_PGAUNW 65 #endif +#define MSG_REMLASTP 66 +#define MSG_ACTPOOL 67 +#define MSG_REMPOOL 68 + enum code_severity { SEVERITY_ERR, SEVERITY_WARN, @@ -456,6 +460,9 @@ struct CODES { { SEVERITY_ERR, MSG_INVPDP, PARAM_STR, "Invalid addpool details '%s'" }, { SEVERITY_ERR, MSG_TOOMANYP,PARAM_NONE, "Reached maximum number of pools (%d)" }, { SEVERITY_SUCC, MSG_ADDPOOL, PARAM_STR, "Added pool '%s'" }, + { SEVERITY_ERR, MSG_REMLASTP,PARAM_POOL, "Cannot remove last pool %d:'%s'" }, + { SEVERITY_ERR, MSG_ACTPOOL, PARAM_POOL, "Cannot remove active pool %d:'%s'" }, + { SEVERITY_SUCC, MSG_REMPOOL, PARAM_BOTH, "Removed pool %d:'%s'" }, { SEVERITY_SUCC, MSG_NOTIFY, PARAM_NONE, "Notify" }, { SEVERITY_FAIL, 0, 0, NULL } }; @@ -483,6 +490,68 @@ extern struct device_api bitforce_api; extern struct device_api icarus_api; #endif +// This is only called when expected to be needed (rarely) +// i.e. strings outside of the codes control (input from the user) +static char *escape_string(char *str, bool isjson) +{ + char *buf, *ptr; + int count; + + count = 0; + for (ptr = str; *ptr; ptr++) { + switch (*ptr) { + case ',': + case '|': + case '=': + if (!isjson) + count++; + break; + case '"': + if (isjson) + count++; + break; + case '\\': + count++; + break; + } + } + + if (count == 0) + return str; + + buf = malloc(strlen(str) + count + 1); + if (unlikely(!buf)) + quit(1, "Failed to malloc escape buf"); + + ptr = buf; + while (*str) + switch (*str) { + case ',': + case '|': + case '=': + if (!isjson) + *(ptr++) = '\\'; + *(ptr++) = *(str++); + break; + case '"': + if (isjson) + *(ptr++) = '\\'; + *(ptr++) = *(str++); + break; + case '\\': + *(ptr++) = '\\'; + *(ptr++) = *(str++); + break; + default: + *(ptr++) = *(str++); + break; + } + + *ptr = '\0'; + + return buf; +} + #if defined(USE_BITFORCE) || defined(USE_ICARUS) static int numpgas() { @@ -1102,6 +1171,8 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, { char buf[BUFSIZ]; char *status, *lp; + char *rpc_url; + char *rpc_user; int i; if (total_pools == 0) { @@ -1133,27 +1204,40 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, else lp = (char *)NO; + rpc_url = escape_string(pool->rpc_url, isjson); + rpc_user = escape_string(pool->rpc_user, isjson); + if (isjson) - sprintf(buf, "%s{\"POOL\":%d,\"URL\":\"%s\",\"Status\":\"%s\",\"Priority\":%d,\"Long Poll\":\"%s\",\"Getworks\":%d,\"Accepted\":%d,\"Rejected\":%d,\"Discarded\":%d,\"Stale\":%d,\"Get Failures\":%d,\"Remote Failures\":%d}", + sprintf(buf, "%s{\"POOL\":%d,\"URL\":\"%s\",\"Status\":\"%s\",\"Priority\":%d,\"Long Poll\":\"%s\",\"Getworks\":%d,\"Accepted\":%d,\"Rejected\":%d,\"Discarded\":%d,\"Stale\":%d,\"Get Failures\":%d,\"Remote Failures\":%d,\"User\":\"%s\"}", (i > 0) ? COMMA : "", - i, pool->rpc_url, status, pool->prio, lp, + i, rpc_url, status, pool->prio, lp, pool->getwork_requested, pool->accepted, pool->rejected, pool->discarded_work, pool->stale_shares, pool->getfail_occasions, - pool->remotefail_occasions); + pool->remotefail_occasions, + rpc_user); else - sprintf(buf, "POOL=%d,URL=%s,Status=%s,Priority=%d,Long Poll=%s,Getworks=%d,Accepted=%d,Rejected=%d,Discarded=%d,Stale=%d,Get Failures=%d,Remote Failures=%d%c", - i, pool->rpc_url, status, pool->prio, lp, + sprintf(buf, "POOL=%d,URL=%s,Status=%s,Priority=%d,Long Poll=%s,Getworks=%d,Accepted=%d,Rejected=%d,Discarded=%d,Stale=%d,Get Failures=%d,Remote Failures=%d,User=%s%c", + i, rpc_url, status, pool->prio, lp, pool->getwork_requested, pool->accepted, pool->rejected, pool->discarded_work, pool->stale_shares, pool->getfail_occasions, - pool->remotefail_occasions, SEPARATOR); + pool->remotefail_occasions, + rpc_user, SEPARATOR); strcat(io_buffer, buf); + + if (rpc_url != pool->rpc_url) + free(rpc_url); + rpc_url = NULL; + + if (rpc_user != pool->rpc_user) + free(rpc_user); + rpc_user = NULL; } if (isjson) @@ -1443,6 +1527,7 @@ exitsama: static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) { char *url, *user, *pass; + char *ptr; if (param == NULL || *param == '\0') { strcpy(io_buffer, message(MSG_MISPDP, 0, NULL, isjson)); @@ -1450,7 +1535,11 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) } if (!pooldetails(param, &url, &user, &pass)) { - strcpy(io_buffer, message(MSG_INVPDP, 0, param, isjson)); + ptr = escape_string(param, isjson); + strcpy(io_buffer, message(MSG_INVPDP, 0, ptr, isjson)); + if (ptr != param) + free(ptr); + ptr = NULL; return; } @@ -1459,7 +1548,11 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) return; } - strcpy(io_buffer, message(MSG_ADDPOOL, 0, url, isjson)); + ptr = escape_string(url, isjson); + strcpy(io_buffer, message(MSG_ADDPOOL, 0, ptr, isjson)); + if (ptr != url) + free(ptr); + ptr = NULL; } static void enablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1535,6 +1628,57 @@ static void disablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcpy(io_buffer, message(MSG_DISPOOL, id, NULL, isjson)); } +static void removepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +{ + struct pool *pool; + char *rpc_url; + bool dofree = false; + int id; + + if (total_pools == 0) { + strcpy(io_buffer, message(MSG_NOPOOL, 0, NULL, isjson)); + return; + } + + if (param == NULL || *param == '\0') { + strcpy(io_buffer, message(MSG_MISPID, 0, NULL, isjson)); + return; + } + + id = atoi(param); + if (id < 0 || id >= total_pools) { + strcpy(io_buffer, message(MSG_INVPID, id, NULL, isjson)); + return; + } + + if (total_pools <= 1) { + strcpy(io_buffer, message(MSG_REMLASTP, id, NULL, isjson)); + return; + } + + pool = pools[id]; + if (pool == current_pool()) + switch_pools(NULL); + + if (pool == current_pool()) { + strcpy(io_buffer, message(MSG_ACTPOOL, id, NULL, isjson)); + return; + } + + pool->enabled = false; + rpc_url = escape_string(pool->rpc_url, isjson); + if (rpc_url != pool->rpc_url) + dofree = true; + + remove_pool(pool); + + strcpy(io_buffer, message(MSG_REMPOOL, id, rpc_url, isjson)); + + if (dofree) + free(rpc_url); + rpc_url = NULL; +} + static bool splitgpuvalue(char *param, int *gpu, char **value, bool isjson) { int id; @@ -1792,6 +1936,7 @@ static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson) { FILE *fcfg; + char *ptr; if (param == NULL || *param == '\0') { strcpy(io_buffer, message(MSG_MISFN, 0, NULL, isjson)); @@ -1800,14 +1945,22 @@ void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson) fcfg = fopen(param, "w"); if (!fcfg) { - strcpy(io_buffer, message(MSG_BADFN, 0, param, isjson)); + ptr = escape_string(param, isjson); + strcpy(io_buffer, message(MSG_BADFN, 0, ptr, isjson)); + if (ptr != param) + free(ptr); + ptr = NULL; return; } write_config(fcfg); fclose(fcfg); - strcpy(io_buffer, message(MSG_SAVED, 0, param, isjson)); + ptr = escape_string(param, isjson); + strcpy(io_buffer, message(MSG_SAVED, 0, ptr, isjson)); + if (ptr != param) + free(ptr); + ptr = NULL; } struct CMDS { @@ -1839,6 +1992,7 @@ struct CMDS { { "addpool", addpool, true }, { "enablepool", enablepool, true }, { "disablepool", disablepool, true }, + { "removepool", removepool, true }, { "gpuintensity", gpuintensity, true }, { "gpumem", gpumem, true }, { "gpuengine", gpuengine, true }, @@ -1875,7 +2029,7 @@ static void send_result(SOCKETTYPE c, bool isjson) } -static void tidyup() +static void tidyup(void *arg) { bye = 1; @@ -2028,6 +2182,7 @@ void api(int api_thr_id) bool did; int i; + pthread_cleanup_push(tidyup, NULL); my_thr_id = api_thr_id; /* This should be done first to ensure curl has already called WSAStartup() in windows */ @@ -2238,5 +2393,5 @@ void api(int api_thr_id) CLOSESOCKET(c); } die: - tidyup(); + pthread_cleanup_pop(true); } diff --git a/bitstreams/LICENSE.txt b/bitstreams/LICENSE.txt new file mode 100644 index 00000000..99cd2ed9 --- /dev/null +++ b/bitstreams/LICENSE.txt @@ -0,0 +1,24 @@ +All the bitstream files included in this directory that follow the name pattern ztex_*.bit are: + +---- + +Copyright (C) 2009-2011 ZTEX GmbH. +http://www.ztex.de + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see http://www.gnu.org/licenses/. + +---- + +You can find the original sources at the BTCMiner project home page: http://www.ztex.de/btcminer/ + diff --git a/bitstreams/ztex_ufm1_15b1.bit b/bitstreams/ztex_ufm1_15b1.bit new file mode 100644 index 00000000..4ed6f4c8 Binary files /dev/null and b/bitstreams/ztex_ufm1_15b1.bit differ diff --git a/bitstreams/ztex_ufm1_15d1.bit b/bitstreams/ztex_ufm1_15d1.bit new file mode 100644 index 00000000..b87eaf4f Binary files /dev/null and b/bitstreams/ztex_ufm1_15d1.bit differ diff --git a/bitstreams/ztex_ufm1_15d3.bit b/bitstreams/ztex_ufm1_15d3.bit new file mode 100644 index 00000000..78157a53 Binary files /dev/null and b/bitstreams/ztex_ufm1_15d3.bit differ diff --git a/cgminer.c b/cgminer.c index cfb02161..389d3d37 100644 --- a/cgminer.c +++ b/cgminer.c @@ -44,8 +44,8 @@ #include "miner.h" #include "findnonce.h" #include "adl.h" -#include "device-cpu.h" -#include "device-gpu.h" +#include "driver-cpu.h" +#include "driver-opencl.h" #include "bench_block.h" #if defined(unix) @@ -54,7 +54,6 @@ #include #endif - enum workio_commands { WC_GET_WORK, WC_SUBMIT_WORK, @@ -1016,6 +1015,9 @@ static char *opt_verusage_and_exit(const char *extra) #endif #ifdef USE_ICARUS "icarus " +#endif +#ifdef USE_ZTEX + "ztex " #endif "mining support.\n" , packagename); @@ -1162,7 +1164,10 @@ WINDOW *mainwin, *statuswin, *logwin; #endif double total_secs = 0.1; static char statusline[256]; +/* logstart is where the log window should start */ static int devcursor, logstart, logcursor; +/* statusy is where the status window goes up to in cases where it won't fit at startup */ +static int statusy; struct cgpu_info gpus[MAX_GPUDEVICES]; /* Maximum number apparently possible */ struct cgpu_info *cpus; @@ -1203,6 +1208,8 @@ static void get_statline(char *buf, struct cgpu_info *cgpu) sprintf(buf, "%s%d ", cgpu->api->name, cgpu->device_id); if (cgpu->api->get_statline_before) cgpu->api->get_statline_before(buf, cgpu); + else + tailsprintf(buf, " | "); tailsprintf(buf, "(%ds):%.1f (avg):%.1f Mh/s | A:%d R:%d HW:%d U:%.2f/m", opt_log_interval, cgpu->rolling, @@ -1255,7 +1262,7 @@ static void curses_print_status(void) wclrtoeol(statuswin); mvwprintw(statuswin, 5, 0, " Block: %s... Started: %s", current_hash, blocktime); mvwhline(statuswin, 6, 0, '-', 80); - mvwhline(statuswin, logstart - 1, 0, '-', 80); + mvwhline(statuswin, statusy - 1, 0, '-', 80); mvwprintw(statuswin, devcursor - 1, 1, "[P]ool management %s[S]ettings [D]isplay options [Q]uit", have_opencl ? "[G]PU management " : ""); } @@ -1272,28 +1279,34 @@ static void curses_print_devstatus(int thr_id) struct cgpu_info *cgpu = thr_info[thr_id].cgpu; char logline[255]; - cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; + cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; - mvwprintw(statuswin, devcursor + cgpu->cgminer_id, 0, " %s %d: ", cgpu->api->name, cgpu->device_id); + /* Check this isn't out of the window size */ + if (wmove(statuswin,devcursor + cgpu->cgminer_id, 0) == ERR) + return; + wprintw(statuswin, " %s %d: ", cgpu->api->name, cgpu->device_id); if (cgpu->api->get_statline_before) { logline[0] = '\0'; cgpu->api->get_statline_before(logline, cgpu); wprintw(statuswin, "%s", logline); } - if (cgpu->status == LIFE_DEAD) - wprintw(statuswin, "DEAD "); - else if (cgpu->status == LIFE_SICK) - wprintw(statuswin, "SICK "); + else + wprintw(statuswin, " | "); + + if (cgpu->status == LIFE_DEAD) + wprintw(statuswin, "DEAD "); + else if (cgpu->status == LIFE_SICK) + wprintw(statuswin, "SICK "); else if (cgpu->deven == DEV_DISABLED) wprintw(statuswin, "OFF "); else if (cgpu->deven == DEV_RECOVER) wprintw(statuswin, "REST "); else wprintw(statuswin, "%5.1f", cgpu->rolling); - adj_width(cgpu->accepted, &awidth); - adj_width(cgpu->rejected, &rwidth); - adj_width(cgpu->hw_errors, &hwwidth); - adj_width(cgpu->utility, &uwidth); + adj_width(cgpu->accepted, &awidth); + adj_width(cgpu->rejected, &rwidth); + adj_width(cgpu->hw_errors, &hwwidth); + adj_width(cgpu->utility, &uwidth); wprintw(statuswin, "/%5.1fMh/s | A:%*d R:%*d HW:%*d U:%*.2f/m", cgpu->total_mhashes / total_secs, awidth, cgpu->accepted, @@ -1307,7 +1320,7 @@ static void curses_print_devstatus(int thr_id) wprintw(statuswin, "%s", logline); } - wclrtoeol(statuswin); + wclrtoeol(statuswin); } #endif @@ -1322,16 +1335,31 @@ static void print_status(int thr_id) static inline bool change_logwinsize(void) { int x, y, logx, logy; + bool ret = false; getmaxyx(mainwin, y, x); - getmaxyx(logwin, logy, logx); + if (x < 80 || y < 25) + return ret; + + if (y > statusy + 2 && statusy < logstart) { + if (y - 2 < logstart) + statusy = y - 2; + else + statusy = logstart; + logcursor = statusy + 1; + mvwin(logwin, logcursor, 0); + wresize(statuswin, statusy, x); + ret = true; + } + y -= logcursor; + getmaxyx(logwin, logy, logx); /* Detect screen size change */ - if ((x != logx || y != logy) && x >= 80 && y >= 25) { + if (x != logx || y != logy) { wresize(logwin, y, x); - return true; + ret = true; } - return false; + return ret; } static void check_winsizes(void) @@ -1342,7 +1370,12 @@ static void check_winsizes(void) int y, x; x = getmaxx(statuswin); - wresize(statuswin, logstart, x); + if (logstart > LINES - 2) + statusy = LINES - 2; + else + statusy = logstart; + logcursor = statusy + 1; + wresize(statuswin, statusy, x); getmaxyx(mainwin, y, x); y -= logcursor; wresize(logwin, y, x); @@ -1523,6 +1556,8 @@ static bool submit_upstream_work(const struct work *work) res = json_object_get(val, "result"); if (!QUIET) { +#ifndef MIPSEB +// This one segfaults on my router for some reason isblock = regeneratehash(work); if (isblock) found_blocks++; @@ -1530,6 +1565,7 @@ static bool submit_upstream_work(const struct work *work) sprintf(hashshow, "%08lx.%08lx.%08lx%s", (unsigned long)(hash32[7]), (unsigned long)(hash32[6]), (unsigned long)(hash32[5]), isblock ? " BLOCK!" : ""); +#endif } /* Theoretically threads could race when modifying accepted and @@ -1773,8 +1809,7 @@ static void disable_curses(void) static void print_summary(void); -/* This should be the common exit path */ -void kill_work(void) +static void __kill_work(void) { struct thr_info *thr; int i; @@ -1821,11 +1856,37 @@ void kill_work(void) applog(LOG_DEBUG, "Killing off API thread"); thr = &thr_info[api_thr_id]; thr_info_cancel(thr); +} + +/* This should be the common exit path */ +void kill_work(void) +{ + __kill_work(); quit(0, "Shutdown signal received."); } -void quit(int status, const char *format, ...); +static char **initial_args; + +static void clean_up(void); + +void app_restart(void) +{ + applog(LOG_WARNING, "Attempting to restart %s", packagename); + + __kill_work(); + clean_up(); + +#if defined(unix) + if (forkpid > 0) { + kill(forkpid, SIGTERM); + forkpid = 0; + } +#endif + + execv(initial_args[0], initial_args); + applog(LOG_WARNING, "Failed to restart application"); +} static void sighandler(int __maybe_unused sig) { @@ -2355,10 +2416,11 @@ static void display_pool_summary(struct pool *pool) unlock_curses(); } } +#endif /* We can't remove the memory used for this struct pool because there may * still be work referencing it. We just remove it from the pools list */ -static void remove_pool(struct pool *pool) +void remove_pool(struct pool *pool) { int i, last_pool = total_pools - 1; struct pool *other; @@ -2379,7 +2441,6 @@ static void remove_pool(struct pool *pool) pool->pool_no = total_pools; total_pools--; } -#endif void write_config(FILE *fcfg) { @@ -2751,7 +2812,8 @@ static void set_options(void) clear_logwin(); retry: wlogprint("\n[L]ongpoll: %s\n", want_longpoll ? "On" : "Off"); - wlogprint("[Q]ueue: %d\n[S]cantime: %d\n[E]xpiry: %d\n[R]etries: %d\n[P]ause: %d\n[W]rite config file\n", + wlogprint("[Q]ueue: %d\n[S]cantime: %d\n[E]xpiry: %d\n[R]etries: %d\n" + "[P]ause: %d\n[W]rite config file\n[C]gminer restart\n", opt_queue, opt_scantime, opt_expiry, opt_retries, opt_fail_pause); wlogprint("Select an option or any other key to return\n"); input = getch(); @@ -2844,6 +2906,13 @@ retry: fclose(fcfg); goto retry; + } else if (!strncasecmp(&input, "c", 1)) { + wlogprint("Are you sure?\n"); + input = getch(); + if (!strncasecmp(&input, "y", 1)) + app_restart(); + else + clear_logwin(); } else clear_logwin(); @@ -3600,7 +3669,7 @@ void *miner_thread(void *userdata) tv_lastupdate = tv_end; } - if (unlikely(mythr->pause || cgpu->deven == DEV_DISABLED)) { + if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED)) { applog(LOG_WARNING, "Thread %d being disabled", thr_id); mythr->rolling = mythr->cgpu->rolling = 0; applog(LOG_DEBUG, "Popping wakeup ping in miner thread"); @@ -4007,8 +4076,8 @@ static void log_print_status(struct cgpu_info *cgpu) { char logline[255]; - get_statline(logline, cgpu); - applog(LOG_WARNING, "%s", logline); + get_statline(logline, cgpu); + applog(LOG_WARNING, "%s", logline); } static void print_summary(void) @@ -4094,6 +4163,9 @@ static void clean_up(void) #ifdef HAVE_OPENCL clear_adl(nDevs); #endif +#ifdef HAVE_LIBUSB + libusb_exit(NULL); +#endif gettimeofday(&total_tv_end, NULL); #ifdef HAVE_CURSES @@ -4329,6 +4401,7 @@ void enable_curses(void) { cbreak(); noecho(); curses_active = true; + statusy = logstart; unlock_curses(); } #endif @@ -4349,6 +4422,10 @@ extern struct device_api bitforce_api; extern struct device_api icarus_api; #endif +#ifdef USE_ZTEX +extern struct device_api ztex_api; +#endif + static int cgminer_id_count = 0; @@ -4364,7 +4441,32 @@ void enable_device(struct cgpu_info *cgpu) #endif } -int main (int argc, char *argv[]) +struct _cgpu_devid_counter { + char name[4]; + int lastid; + UT_hash_handle hh; +}; + +bool add_cgpu(struct cgpu_info*cgpu) +{ + static struct _cgpu_devid_counter *devids = NULL; + struct _cgpu_devid_counter *d; + + HASH_FIND_STR(devids, cgpu->api->name, d); + if (d) + cgpu->device_id = ++d->lastid; + else + { + d = malloc(sizeof(*d)); + memcpy(d->name, cgpu->api->name, sizeof(d->name)); + cgpu->device_id = d->lastid = 0; + HASH_ADD_STR(devids, name, d); + } + devices[total_devices++] = cgpu; + return true; +} + +int main(int argc, char *argv[]) { struct block *block, *tmpblock; struct work *work, *tmpwork; @@ -4379,6 +4481,14 @@ int main (int argc, char *argv[]) if (unlikely(curl_global_init(CURL_GLOBAL_ALL))) quit(1, "Failed to curl_global_init"); + initial_args = malloc(sizeof(char *) * (argc + 1)); + for (i = 0; i < argc; i++) + initial_args[i] = strdup(argv[i]); + initial_args[argc] = NULL; +#ifdef HAVE_LIBUSB + libusb_init(NULL); +#endif + mutex_init(&hash_lock); mutex_init(&qd_lock); #ifdef HAVE_CURSES @@ -4531,6 +4641,10 @@ int main (int argc, char *argv[]) icarus_api.api_detect(); #endif +#ifdef USE_ZTEX + ztex_api.api_detect(); +#endif + #ifdef WANT_CPUMINE cpu_api.api_detect(); #endif @@ -4538,7 +4652,11 @@ int main (int argc, char *argv[]) if (devices_enabled == -1) { applog(LOG_ERR, "Devices detected:"); for (i = 0; i < total_devices; ++i) { - applog(LOG_ERR, " %2d. %s%d", i, devices[i]->api->name, devices[i]->device_id); + struct cgpu_info *cgpu = devices[i]; + if (cgpu->name) + applog(LOG_ERR, " %2d. %s %d: %s (driver: %s)", i, cgpu->api->name, cgpu->device_id, cgpu->name, cgpu->api->dname); + else + applog(LOG_ERR, " %2d. %s %d (driver: %s)", i, cgpu->api->name, cgpu->device_id, cgpu->api->dname); } quit(0, "%d devices listed", total_devices); } diff --git a/configure.ac b/configure.ac index 88f07afb..79ac8c26 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_maj], [2]) m4_define([v_min], [3]) -m4_define([v_mic], [2]) +m4_define([v_mic], [3]) ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_ver], [v_maj.v_min.v_mic]) m4_define([lt_rev], m4_eval(v_maj + v_min)) @@ -57,11 +57,14 @@ gl_INIT dnl Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS(syslog.h) +AC_CHECK_HEADERS([sys/epoll.h]) AC_FUNC_ALLOCA have_win32=false PTHREAD_FLAGS="-lpthread" +USB_LIBS="" +USB_FLAGS="" DLOPEN_FLAGS="-ldl" OPENCL_LIBS="-lOpenCL" WS2_LIBS="" @@ -207,6 +210,16 @@ if test "x$icarus" = xyes; then fi AM_CONDITIONAL([HAS_ICARUS], [test x$icarus = xyes]) +ztex="no" + +AC_ARG_ENABLE([ztex], + [AC_HELP_STRING([--enable-ztex],[Compile support for Ztex (default disabled)])], + [ztex=$enableval] + ) +if test "x$ztex" = xyes; then + AC_DEFINE([USE_ZTEX], [1], [Defined to 1 if Ztex support is wanted]) +fi +AM_CONDITIONAL([HAS_ZTEX], [test x$ztex = xyes]) curses="auto" @@ -301,6 +314,14 @@ if test "x$bitforce" != xno; then fi AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno]) +if test "x$ztex" != xno; then + AC_CHECK_LIB(usb-1.0, libusb_init, , + AC_MSG_ERROR([Could not find usb library - please install libusb])) + AC_DEFINE([HAVE_LIBUSB], [1], [Defined to 1 if libusb is wanted]) + USB_LIBS="-lusb-1.0" + USB_FLAGS="" +fi + PKG_PROG_PKG_CONFIG() PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.15.6], [AC_DEFINE([CURL_HAS_SOCKOPT], [1], [Defined if version of curl supports sockopts.])], @@ -362,6 +383,8 @@ AC_SUBST(PDCURSES_LIBS) AC_SUBST(WS2_LIBS) AC_SUBST(MATH_LIBS) AC_SUBST(UDEV_LIBS) +AC_SUBST(USB_LIBS) +AC_SUBST(USB_FLAGS) AC_CONFIG_FILES([ Makefile @@ -393,13 +416,13 @@ if test "x$opencl" != xno; then echo " OpenCL...............: FOUND. GPU mining support enabled" else echo " OpenCL...............: NOT FOUND. GPU mining support DISABLED" - if test "x$cpumining$bitforce$icarus" = xnonono; then + if test "x$cpumining$bitforce$icarus$ztex" = xnononono; then AC_MSG_ERROR([No mining configured in]) fi fi else echo " OpenCL...............: Detection overrided. GPU mining support DISABLED" - if test "x$cpumining$bitforce$icarus" = xnonono; then + if test "x$cpumining$bitforce$icarus$ztex" = xnononono; then AC_MSG_ERROR([No mining configured in]) fi fi @@ -427,24 +450,28 @@ else echo " Icarus.FPGAs.........: Disabled" fi +if test "x$ztex" = xyes; then + echo " Ztex.FPGAs...........: Enabled" +else + echo " Ztex.FPGAs...........: Disabled" +fi + if test "x$bitforce" != xno; then echo " libudev.detection....: $libudev" fi -echo if test "x$cpumining" = xyes; then + echo echo " CPU Mining...........: Enabled" echo " ASM.(for CPU mining).: $has_yasm" -else - echo " CPU Mining...........: Disabled" fi echo echo "Compilation............: make (or gmake)" echo " CPPFLAGS.............: $CPPFLAGS" echo " CFLAGS...............: $CFLAGS" -echo " LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS" -echo " LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $OPENCL_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $UDEV_LIBS" +echo " LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS $USB_FLAGS" +echo " LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $OPENCL_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $UDEV_LIBS $USB_LIBS" echo echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')" echo " prefix...............: $prefix" diff --git a/bitforce.c b/driver-bitforce.c similarity index 97% rename from bitforce.c rename to driver-bitforce.c index 1118ee16..f93eb871 100644 --- a/bitforce.c +++ b/driver-bitforce.c @@ -91,8 +91,8 @@ static void BFwrite(int fd, const void *buf, ssize_t bufLen) static bool bitforce_detect_one(const char *devpath) { + char *s; char pdevbuf[0x100]; - static int i = 0; if (total_devices == MAX_DEVICES) return false; @@ -117,14 +117,17 @@ static bool bitforce_detect_one(const char *devpath) // We have a real BitForce! struct cgpu_info *bitforce; bitforce = calloc(1, sizeof(*bitforce)); - devices[total_devices++] = bitforce; bitforce->api = &bitforce_api; - bitforce->device_id = i++; bitforce->device_path = strdup(devpath); bitforce->deven = DEV_ENABLED; bitforce->threads = 1; + if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>")))) + { + s[0] = '\0'; + bitforce->name = strdup(pdevbuf + 7); + } - return true; + return add_cgpu(bitforce); } static bool bitforce_detect_auto_udev() @@ -358,7 +361,8 @@ static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint6 } struct device_api bitforce_api = { - .name = "BFL", + .dname = "bitforce", + .name = "PGA", .api_detect = bitforce_detect, .get_statline_before = get_bitforce_statline_before, .thread_prepare = bitforce_thread_prepare, diff --git a/device-cpu.c b/driver-cpu.c similarity index 99% rename from device-cpu.c rename to driver-cpu.c index 4cc01a89..d0a25160 100644 --- a/device-cpu.c +++ b/driver-cpu.c @@ -32,7 +32,7 @@ #include "compat.h" #include "miner.h" #include "bench_block.h" -#include "device-cpu.h" +#include "driver-cpu.h" #if defined(unix) #include @@ -739,13 +739,13 @@ static void cpu_detect() for (i = 0; i < opt_n_threads; ++i) { struct cgpu_info *cgpu; - cgpu = devices[total_devices + i] = &cpus[i]; + cgpu = &cpus[i]; cgpu->api = &cpu_api; cgpu->deven = DEV_ENABLED; - cgpu->device_id = i; cgpu->threads = 1; + cgpu->kname = algo_names[opt_algo]; + add_cgpu(cgpu); } - total_devices += opt_n_threads; } static void reinit_cpu_device(struct cgpu_info *cpu) @@ -827,6 +827,7 @@ CPUSearch: } struct device_api cpu_api = { + .dname = "cpu", .name = "CPU", .api_detect = cpu_detect, .reinit_device = reinit_cpu_device, diff --git a/device-cpu.h b/driver-cpu.h similarity index 100% rename from device-cpu.h rename to driver-cpu.h diff --git a/icarus.c b/driver-icarus.c similarity index 79% rename from icarus.c rename to driver-icarus.c index 13e31ca1..334dc11a 100644 --- a/icarus.c +++ b/driver-icarus.c @@ -46,11 +46,17 @@ #include #include #endif +#ifdef HAVE_SYS_EPOLL_H + #include + #define HAVE_EPOLL +#endif #include "elist.h" #include "miner.h" -#define ICARUS_READ_FAULT_COUNT (8) +// 8 second timeout +#define ICARUS_READ_FAULT_DECISECONDS (1) +#define ICARUS_READ_FAULT_COUNT (80) struct device_api icarus_api; @@ -87,7 +93,7 @@ static int icarus_open(const char *devpath) ISTRIP | INLCR | IGNCR | ICRNL | IXON); my_termios.c_oflag &= ~OPOST; my_termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - my_termios.c_cc[VTIME] = 10; /* block 1 second */ + my_termios.c_cc[VTIME] = ICARUS_READ_FAULT_DECISECONDS; my_termios.c_cc[VMIN] = 0; tcsetattr(serialfd, TCSANOW, &my_termios); @@ -108,12 +114,31 @@ static int icarus_open(const char *devpath) #endif } -static int icarus_gets(unsigned char *buf, size_t bufLen, int fd) +static int icarus_gets(unsigned char *buf, size_t bufLen, int fd, volatile unsigned long *wr) { ssize_t ret = 0; int rc = 0; + int epollfd = -1; + +#ifdef HAVE_EPOLL + struct epoll_event ev, evr; + epollfd = epoll_create(1); + if (epollfd != -1) { + ev.events = EPOLLIN; + ev.data.fd = fd; + if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) { + close(epollfd); + epollfd = -1; + } + } +#endif while (bufLen) { +#ifdef HAVE_EPOLL + if (epollfd != -1 && epoll_wait(epollfd, &evr, 1, ICARUS_READ_FAULT_DECISECONDS * 100) != 1) + ret = 0; + else +#endif ret = read(fd, buf, 1); if (ret == 1) { bufLen--; @@ -122,13 +147,20 @@ static int icarus_gets(unsigned char *buf, size_t bufLen, int fd) } rc++; + if (*wr) + return 1; if (rc == ICARUS_READ_FAULT_COUNT) { + if (epollfd != -1) + close(epollfd); applog(LOG_DEBUG, - "Icarus Read: No data in %d seconds", rc); + "Icarus Read: No data in %d seconds", rc * ICARUS_READ_FAULT_DECISECONDS / 10); return 1; } } + if (epollfd != -1) + close(epollfd); + return 0; } @@ -172,7 +204,8 @@ static bool icarus_detect_one(const char *devpath) icarus_write(fd, ob_bin, sizeof(ob_bin)); memset(nonce_bin, 0, sizeof(nonce_bin)); - icarus_gets(nonce_bin, sizeof(nonce_bin), fd); + volatile unsigned long wr = 0; + icarus_gets(nonce_bin, sizeof(nonce_bin), fd, &wr); icarus_close(fd); @@ -194,10 +227,9 @@ static bool icarus_detect_one(const char *devpath) struct cgpu_info *icarus; icarus = calloc(1, sizeof(struct cgpu_info)); icarus->api = &icarus_api; - icarus->device_id = total_devices; icarus->device_path = strdup(devpath); icarus->threads = 1; - devices[total_devices++] = icarus; + add_cgpu(icarus); applog(LOG_INFO, "Found Icarus at %s, mark as %d", devpath, icarus->device_id); @@ -235,6 +267,8 @@ static bool icarus_prepare(struct thr_info *thr) static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, __maybe_unused uint64_t max_nonce) { + volatile unsigned long *wr = &work_restart[thr->id].restart; + struct cgpu_info *icarus; int fd; int ret; @@ -243,7 +277,7 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, char *ob_hex, *nonce_hex; uint32_t nonce; uint32_t hash_count; - time_t t = 0; + struct timeval tv_start, tv_end, diff; icarus = thr->cgpu; @@ -262,6 +296,9 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, #ifndef WIN32 tcflush(fd, TCOFLUSH); #endif + + gettimeofday(&tv_start, NULL); + ret = icarus_write(fd, ob_bin, sizeof(ob_bin)); if (ret) { icarus_close(fd); @@ -270,7 +307,6 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, ob_hex = bin2hex(ob_bin, sizeof(ob_bin)); if (ob_hex) { - t = time(NULL); applog(LOG_DEBUG, "Icarus %s send: %s", icarus->device_id, ob_hex); free(ob_hex); @@ -278,27 +314,35 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, /* Icarus will return 8 bytes nonces or nothing */ memset(nonce_bin, 0, sizeof(nonce_bin)); - ret = icarus_gets(nonce_bin, sizeof(nonce_bin), fd); + ret = icarus_gets(nonce_bin, sizeof(nonce_bin), fd, wr); + + gettimeofday(&tv_end, NULL); + timeval_subtract(&diff, &tv_end, &tv_start); nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); if (nonce_hex) { - t = time(NULL) - t; - applog(LOG_DEBUG, "Icarus %d return (elapse %d seconds): %s", - icarus->device_id, t, nonce_hex); + applog(LOG_DEBUG, "Icarus %d returned (in %d.%06d seconds): %s", + icarus->device_id, diff.tv_sec, diff.tv_usec, nonce_hex); free(nonce_hex); } memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin)); + work->blk.nonce = 0xffffffff; + icarus_close(fd); + if (nonce == 0 && ret) { - icarus_close(fd); - return 0xffffffff; + if (unlikely(diff.tv_sec > 12 || (diff.tv_sec == 11 && diff.tv_usec > 300067))) + return 0xffffffff; + // Approximately how much of the nonce Icarus scans in 1 second... + // 0x16a7a561 would be if it was exactly 380 MH/s + // 0x168b7b4b was the average over a 201-sample period based on time to find actual shares + return (0x168b7b4b * diff.tv_sec) + (0x17a * diff.tv_usec); } #ifndef __BIG_ENDIAN__ nonce = swab32(nonce); #endif - work->blk.nonce = 0xffffffff; submit_nonce(thr, work, nonce); hash_count = (nonce & 0x7fffffff); @@ -311,7 +355,8 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, hash_count <<= 1; } - icarus_close(fd); + applog(LOG_DEBUG, "0x%x hashes in %d.%06d seconds", hash_count, diff.tv_sec, diff.tv_usec); + return hash_count; } @@ -333,7 +378,8 @@ static void icarus_shutdown(struct thr_info *thr) } struct device_api icarus_api = { - .name = "ICA", + .dname = "icarus", + .name = "PGA", .api_detect = icarus_detect, .thread_prepare = icarus_prepare, .scanhash = icarus_scanhash, diff --git a/device-gpu.c b/driver-opencl.c similarity index 98% rename from device-gpu.c rename to driver-opencl.c index 917d9560..da9a597c 100644 --- a/device-gpu.c +++ b/driver-opencl.c @@ -28,7 +28,7 @@ #include "compat.h" #include "miner.h" -#include "device-gpu.h" +#include "driver-opencl.h" #include "findnonce.h" #include "ocl.h" #include "adl.h" @@ -1107,12 +1107,13 @@ static void opencl_detect() for (i = 0; i < nDevs; ++i) { struct cgpu_info *cgpu; - cgpu = devices[total_devices++] = &gpus[i]; + cgpu = &gpus[i]; cgpu->deven = DEV_ENABLED; cgpu->api = &opencl_api; cgpu->device_id = i; cgpu->threads = opt_g_threads; cgpu->virtual_gpu = i; + add_cgpu(cgpu); } if (!opt_noadl) @@ -1179,6 +1180,7 @@ static bool opencl_thread_prepare(struct thr_info *thr) return false; } + strcpy(name, ""); applog(LOG_INFO, "Init GPU thread %i GPU %i virtual GPU %i", i, gpu, virtual_gpu); clStates[i] = initCl(virtual_gpu, name, sizeof(name)); if (!clStates[i]) { @@ -1208,6 +1210,26 @@ static bool opencl_thread_prepare(struct thr_info *thr) return false; } + if (!cgpu->name) + cgpu->name = strdup(name); + if (!cgpu->kname) + { + switch (clStates[i]->chosen_kernel) { + case KL_DIABLO: + cgpu->kname = "diablo"; + break; + case KL_DIAKGCN: + cgpu->kname = "diakgcn"; + break; + case KL_PHATK: + cgpu->kname = "phatk"; + break; + case KL_POCLBM: + cgpu->kname = "poclbm"; + default: + break; + } + } applog(LOG_INFO, "initCl() finished. Found %s", name); gettimeofday(&now, NULL); get_datestamp(cgpu->init, &now); @@ -1404,6 +1426,7 @@ static void opencl_thread_shutdown(struct thr_info *thr) } struct device_api opencl_api = { + .dname = "opencl", .name = "GPU", .api_detect = opencl_detect, .reinit_device = reinit_opencl_device, diff --git a/device-gpu.h b/driver-opencl.h similarity index 100% rename from device-gpu.h rename to driver-opencl.h diff --git a/driver-ztex.c b/driver-ztex.c new file mode 100644 index 00000000..03b409f1 --- /dev/null +++ b/driver-ztex.c @@ -0,0 +1,322 @@ +/** + * ztex.c - cgminer worker for Ztex 1.15x fpga board + * + * Copyright (c) 2012 nelisky.btc@gmail.com + * + * This work is based upon the Java SDK provided by ztex which is + * Copyright (C) 2009-2011 ZTEX GmbH. + * http://www.ztex.de + * + * This work is based upon the icarus.c worker which is + * Copyright 2012 Luke Dashjr + * Copyright 2012 Xiangfu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. +**/ +#include +#include +#include "miner.h" +#include "libztex.h" + +#define GOLDEN_BACKLOG 5 + +struct device_api ztex_api, ztex_hotplug_api; + +// Forward declarations +static void ztex_disable(struct thr_info* thr); +static bool ztex_prepare(struct thr_info *thr); + +static void ztex_detect(void) +{ + int cnt; + int i; + struct libztex_dev_list **ztex_devices; + + cnt = libztex_scanDevices(&ztex_devices); + applog(LOG_WARNING, "Found %d ztex board(s)", cnt); + + for (i = 0; i < cnt; i++) { + if (total_devices == MAX_DEVICES) + break; + struct cgpu_info *ztex; + ztex = calloc(1, sizeof(struct cgpu_info)); + ztex->api = &ztex_api; + ztex->device_ztex = ztex_devices[i]->dev; + ztex->threads = 1; + add_cgpu(ztex); + + applog(LOG_WARNING,"%s: Found Ztex, mark as %d", ztex->device_ztex->repr, ztex->device_id); + } + + if (cnt > 0) + libztex_freeDevList(ztex_devices); +} + +static bool ztex_updateFreq(struct libztex_device* ztex) +{ + int i, maxM, bestM; + double bestR, r; + + for (i = 0; i < ztex->freqMaxM; i++) + if (ztex->maxErrorRate[i + 1] * i < ztex->maxErrorRate[i] * (i + 20)) + ztex->maxErrorRate[i + 1] = ztex->maxErrorRate[i] * (1.0 + 20.0 / i); + + maxM = 0; + while (maxM < ztex->freqMDefault && ztex->maxErrorRate[maxM + 1] < LIBZTEX_MAXMAXERRORRATE) + maxM++; + while (maxM < ztex->freqMaxM && ztex->errorWeight[maxM] > 150 && ztex->maxErrorRate[maxM + 1] < LIBZTEX_MAXMAXERRORRATE) + maxM++; + + bestM = 0; + bestR = 0; + for (i = 0; i <= maxM; i++) { + r = (i + 1 + (i == ztex->freqM? LIBZTEX_ERRORHYSTERESIS: 0)) * (1 - ztex->maxErrorRate[i]); + if (r > bestR) { + bestM = i; + bestR = r; + } + } + + if (bestM != ztex->freqM) + libztex_setFreq(ztex, bestM); + + maxM = ztex->freqMDefault; + while (maxM < ztex->freqMaxM && ztex->errorWeight[maxM + 1] > 100) + maxM++; + if ((bestM < (1.0 - LIBZTEX_OVERHEATTHRESHOLD) * maxM) && bestM < maxM - 1) { + libztex_resetFpga(ztex); + applog(LOG_ERR, "%s: frequency drop of %.1f%% detect. This may be caused by overheating. FPGA is shut down to prevent damage.", + ztex->repr, (1.0 - 1.0 * bestM / maxM) * 100); + return false; + } + return true; +} + + +static bool ztex_checkNonce(struct libztex_device *ztex, + struct work *work, + struct libztex_hash_data *hdata) +{ + uint32_t *data32 = (uint32_t *)(work->data); + unsigned char swap[128]; + uint32_t *swap32 = (uint32_t *)swap; + unsigned char hash1[32]; + unsigned char hash2[32]; + uint32_t *hash2_32 = (uint32_t *)hash2; + int i; + +#if defined(__BIGENDIAN__) || defined(MIPSEB) + hdata->nonce = swab32(hdata->nonce); + hdata->hash7 = swab32(hdata->hash7); +#endif + + work->data[64 + 12 + 0] = (hdata->nonce >> 0) & 0xff; + work->data[64 + 12 + 1] = (hdata->nonce >> 8) & 0xff; + work->data[64 + 12 + 2] = (hdata->nonce >> 16) & 0xff; + work->data[64 + 12 + 3] = (hdata->nonce >> 24) & 0xff; + + for (i = 0; i < 80 / 4; i++) + swap32[i] = swab32(data32[i]); + + sha2(swap, 80, hash1, false); + sha2(hash1, 32, hash2, false); +#if defined(__BIGENDIAN__) || defined(MIPSEB) + if (hash2_32[7] != ((hdata->hash7 + 0x5be0cd19) & 0xFFFFFFFF)) { +#else + if (swab32(hash2_32[7]) != ((hdata->hash7 + 0x5be0cd19) & 0xFFFFFFFF)) { +#endif + ztex->errorCount[ztex->freqM] += 1.0 / ztex->numNonces; + applog(LOG_DEBUG, "%s: checkNonce failed for %0.8X", ztex->repr, hdata->nonce); + return false; + } + return true; +} + +static uint64_t ztex_scanhash(struct thr_info *thr, struct work *work, + __maybe_unused uint64_t max_nonce) +{ + struct libztex_device *ztex; + unsigned char sendbuf[44]; + int i, j; + uint32_t backlog[GOLDEN_BACKLOG]; + int backlog_p = 0; + uint32_t lastnonce[GOLDEN_BACKLOG], nonce, noncecnt = 0; + bool overflow, found, rv; + struct libztex_hash_data hdata[GOLDEN_BACKLOG]; + + ztex = thr->cgpu->device_ztex; + + memcpy(sendbuf, work->data + 64, 12); + memcpy(sendbuf + 12, work->midstate, 32); + memset(backlog, 0, sizeof(backlog)); + i = libztex_sendHashData(ztex, sendbuf); + if (i < 0) { + // Something wrong happened in send + applog(LOG_ERR, "%s: Failed to send hash data with err %d, retrying", ztex->repr, i); + usleep(500000); + i = libztex_sendHashData(ztex, sendbuf); + if (i < 0) { + // And there's nothing we can do about it + ztex_disable(thr); + applog(LOG_ERR, "%s: Failed to send hash data with err %d, giving up", ztex->repr, i); + return 0; + } + } + + applog(LOG_DEBUG, "sent hashdata"); + + for (i = 0; i < ztex->numNonces; i++) + lastnonce[i] = 0; + + overflow = false; + + while (!(overflow || work_restart[thr->id].restart)) { + usleep(250000); + if (work_restart[thr->id].restart) { + applog(LOG_DEBUG, "%s: New work detected", ztex->repr); + break; + } + i = libztex_readHashData(ztex, &hdata[0]); + if (i < 0) { + // Something wrong happened in read + applog(LOG_ERR, "%s: Failed to read hash data with err %d, retrying", ztex->repr, i); + usleep(500000); + i = libztex_readHashData(ztex, &hdata[0]); + if (i < 0) { + // And there's nothing we can do about it + ztex_disable(thr); + applog(LOG_ERR, "%s: Failed to read hash data with err %d, giving up", ztex->repr, i); + return 0; + } + } + + if (work_restart[thr->id].restart) { + applog(LOG_DEBUG, "%s: New work detected", ztex->repr); + break; + } + + ztex->errorCount[ztex->freqM] *= 0.995; + ztex->errorWeight[ztex->freqM] = ztex->errorWeight[ztex->freqM] * 0.995 + 1.0; + + for (i = 0; i < ztex->numNonces; i++) { + nonce = hdata[i].nonce; +#if defined(__BIGENDIAN__) || defined(MIPSEB) + nonce = swab32(nonce); +#endif + if (nonce > noncecnt) + noncecnt = nonce; + if (((nonce & 0x7fffffff) >> 4) < ((lastnonce[i] & 0x7fffffff) >> 4)) { + applog(LOG_DEBUG, "%s: overflow nonce=%0.8x lastnonce=%0.8x", ztex->repr, nonce, lastnonce[i]); + overflow = true; + } else + lastnonce[i] = nonce; +#if !(defined(__BIGENDIAN__) || defined(MIPSEB)) + nonce = swab32(nonce); +#endif + if (!ztex_checkNonce(ztex, work, &hdata[i])) { + thr->cgpu->hw_errors++; + continue; + } + nonce = hdata[i].goldenNonce; + if (nonce > 0) { + found = false; + for (j = 0; j < GOLDEN_BACKLOG; j++) { + if (backlog[j] == nonce) { + found = true; + break; + } + } + if (!found) { + applog(LOG_DEBUG, "%s: Share found", ztex->repr); + backlog[backlog_p++] = nonce; + if (backlog_p >= GOLDEN_BACKLOG) + backlog_p = 0; +#if defined(__BIGENDIAN__) || defined(MIPSEB) + nonce = swab32(nonce); +#endif + work->blk.nonce = 0xffffffff; + rv = submit_nonce(thr, work, nonce); + applog(LOG_DEBUG, "%s: submitted %0.8x %d", ztex->repr, nonce, rv); + } + } + + } + + } + + ztex->errorRate[ztex->freqM] = ztex->errorCount[ztex->freqM] / ztex->errorWeight[ztex->freqM] * (ztex->errorWeight[ztex->freqM] < 100? ztex->errorWeight[ztex->freqM] * 0.01: 1.0); + if (ztex->errorRate[ztex->freqM] > ztex->maxErrorRate[ztex->freqM]) + ztex->maxErrorRate[ztex->freqM] = ztex->errorRate[ztex->freqM]; + + if (!ztex_updateFreq(ztex)) + // Something really serious happened, so mark this thread as dead! + return 0; + + applog(LOG_DEBUG, "%s: exit %1.8X", ztex->repr, noncecnt); + + work->blk.nonce = 0xffffffff; + + return noncecnt > 0? noncecnt: 1; +} + +static void ztex_statline_before(char *buf, struct cgpu_info *cgpu) +{ + if (cgpu->deven == DEV_ENABLED) { + tailsprintf(buf, "%s | ", cgpu->device_ztex->snString); + tailsprintf(buf, "%0.2fMhz | ", cgpu->device_ztex->freqM1 * (cgpu->device_ztex->freqM + 1)); + } +} + +static bool ztex_prepare(struct thr_info *thr) +{ + struct timeval now; + struct cgpu_info *ztex = thr->cgpu; + + gettimeofday(&now, NULL); + get_datestamp(ztex->init, &now); + + if (libztex_configureFpga(ztex->device_ztex) != 0) + return false; + + ztex->device_ztex->freqM = -1; + ztex_updateFreq(ztex->device_ztex); + + applog(LOG_DEBUG, "%s: prepare", ztex->device_ztex->repr); + return true; +} + +static void ztex_shutdown(struct thr_info *thr) +{ + if (thr->cgpu->device_ztex != NULL) { + applog(LOG_DEBUG, "%s: shutdown", thr->cgpu->device_ztex->repr); + libztex_destroy_device(thr->cgpu->device_ztex); + thr->cgpu->device_ztex = NULL; + } +} + +static void ztex_disable(struct thr_info *thr) +{ + applog(LOG_ERR, "%s: Disabling!", thr->cgpu->device_ztex->repr); + devices[thr->cgpu->device_id]->deven = DEV_DISABLED; + ztex_shutdown(thr); +} + +struct device_api ztex_api = { + .dname = "ztex", + .name = "PGA", + .api_detect = ztex_detect, + .get_statline_before = ztex_statline_before, + .thread_prepare = ztex_prepare, + .scanhash = ztex_scanhash, + .thread_shutdown = ztex_shutdown, +}; diff --git a/libztex.c b/libztex.c new file mode 100644 index 00000000..d76d7987 --- /dev/null +++ b/libztex.c @@ -0,0 +1,477 @@ +/** + * libztex.c - Ztex 1.15x fpga board support library + * + * Copyright (c) 2012 nelisky.btc@gmail.com + * + * This work is based upon the Java SDK provided by ztex which is + * Copyright (C) 2009-2011 ZTEX GmbH. + * http://www.ztex.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. +**/ + +#include +#include +#include "miner.h" +#include "libztex.h" + +#define BUFSIZE 256 + +//* Capability index for EEPROM support. +#define CAPABILITY_EEPROM 0,0 +//* Capability index for FPGA configuration support. +#define CAPABILITY_FPGA 0,1 +//* Capability index for FLASH memory support. +#define CAPABILITY_FLASH 0,2 +//* Capability index for DEBUG helper support. +#define CAPABILITY_DEBUG 0,3 +//* Capability index for AVR XMEGA support. +#define CAPABILITY_XMEGA 0,4 +//* Capability index for AVR XMEGA support. +#define CAPABILITY_HS_FPGA 0,5 +//* Capability index for AVR XMEGA support. +#define CAPABILITY_MAC_EEPROM 0,6 + + + +static bool libztex_checkDevice(struct libusb_device *dev) +{ + struct libusb_device_descriptor desc; + int err; + + err = libusb_get_device_descriptor(dev, &desc); + if (unlikely(err != 0)) { + applog(LOG_ERR, "Ztex check device: Failed to open read descriptor with error %d", err); + return false; + } + if (!(desc.idVendor == LIBZTEX_IDVENDOR && desc.idProduct == LIBZTEX_IDPRODUCT)) { + applog(LOG_DEBUG, "Not a ZTEX device %0.4x:%0.4x", desc.idVendor, desc.idProduct); + return false; + } + return true; +} + +static bool libztex_checkCapability(struct libztex_device *ztex, int i, int j) +{ + if (!((i >= 0) && (i <= 5) && (j >= 0) && (j < 8) && + (((ztex->interfaceCapabilities[i] & 255) & (1 << j)) != 0))) { + applog(LOG_ERR, "%s: capability missing: %d %d", ztex->repr, i, i); + return false; + } + return true; +} + +static int libztex_detectBitstreamBitOrder(const unsigned char *buf, int size) +{ + int i; + + for (i = 0; i < size - 4; i++) { + if (((buf[i] & 255) == 0xaa) && ((buf[i + 1] & 255) == 0x99) && ((buf[i + 2] & 255) == 0x55) && ((buf[i + 3] & 255) == 0x66)) + return 1; + if (((buf[i] & 255) == 0x55) && ((buf[i + 1] & 255) == 0x99) && ((buf[i + 2] & 255) == 0xaa) && ((buf[i + 3] & 255) == 0x66)) + return 0; + } + applog(LOG_WARNING, "Unable to determine bitstream bit order: no signature found"); + return 0; +} + +static void libztex_swapBits(unsigned char *buf, int size) +{ + unsigned char c; + int i; + + for (i = 0; i < size; i++) { + c = buf[i]; + buf[i] = ((c & 128) >> 7) | + ((c & 64) >> 5) | + ((c & 32) >> 3) | + ((c & 16) >> 1) | + ((c & 8) << 1) | + ((c & 4) << 3) | + ((c & 2) << 5) | + ((c & 1) << 7); + } +} + +static int libztex_getFpgaState(struct libztex_device *ztex, struct libztex_fpgastate *state) +{ + unsigned char buf[9]; + int cnt; + + if (!libztex_checkCapability(ztex, CAPABILITY_FPGA)) + return -1; + cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x30, 0, 0, buf, 9, 1000); + if (unlikely(cnt < 0)) { + applog(LOG_ERR, "%s: Failed getFpgaState with err %d", ztex->repr, cnt); + return cnt; + } + state->fpgaConfigured = (buf[0] == 0); + state->fpgaChecksum = buf[1] & 0xff; + state->fpgaBytes = ((buf[5] & 0xff) << 24) | ((buf[4] & 0xff) << 16) | ((buf[3] & 0xff) << 8) | (buf[2] & 0xff); + state->fpgaInitB = buf[6] & 0xff; + state->fpgaFlashResult = buf[7]; + state->fpgaFlashBitSwap = (buf[8] != 0); + return 0; +} + +static int libztex_configureFpgaLS(struct libztex_device *ztex, const char* firmware, bool force, char bs) +{ + struct libztex_fpgastate state; + const int transactionBytes = 2048; + unsigned char buf[transactionBytes], cs; + int tries, cnt, buf_p, i; + ssize_t pos = 0; + FILE *fp; + + if (!libztex_checkCapability(ztex, CAPABILITY_FPGA)) + return -1; + + libztex_getFpgaState(ztex, &state); + if (!force && state.fpgaConfigured) { + applog(LOG_DEBUG, "Bitstream already configured"); + return 1; + } + + for (tries = 10; tries > 0; tries--) { + fp = fopen(firmware, "rb"); + if (!fp) { + applog(LOG_ERR, "%s: failed to read firmware '%s'", ztex->repr, firmware); + return -2; + } + + cs = 0; + while (pos < transactionBytes && !feof(fp)) { + buf[pos] = getc(fp); + cs += buf[pos++]; + } + + if (feof(fp)) + pos--; + + if (bs != 0 && bs != 1) + bs = libztex_detectBitstreamBitOrder(buf, transactionBytes < pos? transactionBytes: pos); + + //* Reset fpga + cnt = libztex_resetFpga(ztex); + if (unlikely(cnt < 0)) { + applog(LOG_ERR, "%s: Failed reset fpga with err %d", ztex->repr, cnt); + continue; + } + + if (bs == 1) + libztex_swapBits(buf, pos); + + buf_p = pos; + while (1) { + i = 0; + while (i < buf_p) { + cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x32, 0, 0, &buf[i], buf_p - i, 5000); + if (unlikely(cnt < 0)) { + applog(LOG_ERR, "%s: Failed send fpga data with err %d", ztex->repr, cnt); + break; + } + i += cnt; + } + if (i < buf_p || buf_p < transactionBytes) + break; + buf_p = 0; + while (buf_p < transactionBytes && !feof(fp)) { + buf[buf_p] = getc(fp); + cs += buf[buf_p++]; + } + if (feof(fp)) + buf_p--; + pos += buf_p; + if (buf_p == 0) + break; + if (bs == 1) + libztex_swapBits(buf, buf_p); + } + if (cnt >= 0) + tries = 0; + + fclose(fp); + } + libztex_getFpgaState(ztex, &state); + if (!state.fpgaConfigured) { + applog(LOG_ERR, "%s: FPGA configuration failed: DONE pin does not go high", ztex->repr); + return 3; + } + usleep(200000); + applog(LOG_INFO, "%s: FPGA configuration done", ztex->repr); + return 0; +} + +int libztex_configureFpga(struct libztex_device *ztex) +{ + char buf[256] = "bitstreams/"; + + memset(&buf[11], 0, 245); + strcpy(&buf[11], ztex->bitFileName); + strcpy(&buf[strlen(buf)], ".bit"); + + return libztex_configureFpgaLS(ztex, buf, true, 2); +} + +int libztex_setFreq(struct libztex_device *ztex, uint16_t freq) +{ + int cnt; + + if (freq > ztex->freqMaxM) + freq = ztex->freqMaxM; + + cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x83, freq, 0, NULL, 0, 500); + if (unlikely(cnt < 0)) { + applog(LOG_ERR, "Ztex check device: Failed to set frequency with err %d", cnt); + return cnt; + } + ztex->freqM = freq; + applog(LOG_WARNING, "%s: Frequency change to %0.2f Mhz", ztex->repr, ztex->freqM1 * (ztex->freqM + 1)); + + return 0; +} + +int libztex_resetFpga(struct libztex_device *ztex) +{ + return libusb_control_transfer(ztex->hndl, 0x40, 0x31, 0, 0, NULL, 0, 1000); +} + +int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** ztex) +{ + struct libztex_device *newdev; + unsigned char buf[64]; + int cnt, err; + + newdev = malloc(sizeof(struct libztex_device)); + newdev->bitFileName = NULL; + newdev->valid = false; + newdev->hndl = NULL; + *ztex = newdev; + + err = libusb_get_device_descriptor(dev, &newdev->descriptor); + if (unlikely(err != 0)) { + applog(LOG_ERR, "Ztex check device: Failed to open read descriptor with error %d", err); + return err; + } + + // Check vendorId and productId + if (!(newdev->descriptor.idVendor == LIBZTEX_IDVENDOR && + newdev->descriptor.idProduct == LIBZTEX_IDPRODUCT)) { + applog(LOG_ERR, "Not a ztex device? %0.4X, %0.4X", newdev->descriptor.idVendor, newdev->descriptor.idProduct); + return 1; + } + + err = libusb_open(dev, &newdev->hndl); + if (unlikely(err != 0)) { + applog(LOG_ERR, "Ztex check device: Failed to open handle with error %d", err); + return err; + } + + cnt = libusb_get_string_descriptor_ascii (newdev->hndl, newdev->descriptor.iSerialNumber, newdev->snString, + LIBZTEX_SNSTRING_LEN + 1); + if (unlikely(cnt < 0)) { + applog(LOG_ERR, "Ztex check device: Failed to read device snString with err %d", cnt); + return cnt; + } + + cnt = libusb_control_transfer(newdev->hndl, 0xc0, 0x22, 0, 0, buf, 40, 500); + if (unlikely(cnt < 0)) { + applog(LOG_ERR, "Ztex check device: Failed to read ztex descriptor with err %d", cnt); + return cnt; + } + + if ( buf[0] != 40 || buf[1] != 1 || buf[2] != 'Z' || buf[3] != 'T' || buf[4] != 'E' || buf[5] != 'X' ) { + applog(LOG_ERR, "Ztex check device: Error reading ztex descriptor"); + return 2; + } + + newdev->productId[0] = buf[6]; + newdev->productId[1] = buf[7]; + newdev->productId[2] = buf[8]; + newdev->productId[3] = buf[9]; + newdev->fwVersion = buf[10]; + newdev->interfaceVersion = buf[11]; + newdev->interfaceCapabilities[0] = buf[12]; + newdev->interfaceCapabilities[1] = buf[13]; + newdev->interfaceCapabilities[2] = buf[14]; + newdev->interfaceCapabilities[3] = buf[15]; + newdev->interfaceCapabilities[4] = buf[16]; + newdev->interfaceCapabilities[5] = buf[17]; + newdev->moduleReserved[0] = buf[18]; + newdev->moduleReserved[1] = buf[19]; + newdev->moduleReserved[2] = buf[20]; + newdev->moduleReserved[3] = buf[21]; + newdev->moduleReserved[4] = buf[22]; + newdev->moduleReserved[5] = buf[23]; + newdev->moduleReserved[6] = buf[24]; + newdev->moduleReserved[7] = buf[25]; + newdev->moduleReserved[8] = buf[26]; + newdev->moduleReserved[9] = buf[27]; + newdev->moduleReserved[10] = buf[28]; + newdev->moduleReserved[11] = buf[29]; + + + cnt = libusb_control_transfer(newdev->hndl, 0xc0, 0x82, 0, 0, buf, 64, 500); + if (unlikely(cnt < 0)) { + applog(LOG_ERR, "Ztex check device: Failed to read ztex descriptor with err %d", cnt); + return cnt; + } + + if (unlikely(buf[0] != 4)) { + if (unlikely(buf[0] != 2)) { + applog(LOG_ERR, "Invalid BTCMiner descriptor version. Firmware must be updated (%d).", buf[0]); + return 3; + } + applog(LOG_WARNING, "Firmware out of date"); + } + + newdev->numNonces = buf[1] + 1; + newdev->offsNonces = ((buf[2] & 255) | ((buf[3] & 255) << 8)) - 10000; + newdev->freqM1 = ((buf[4] & 255) | ((buf[5] & 255) << 8) ) * 0.01; + newdev->freqMaxM = (buf[7] & 255); + newdev->freqM = (buf[6] & 255); + newdev->freqMDefault = newdev->freqM; + + for (cnt=0; cnt < 255; cnt++) { + newdev->errorCount[cnt] = 0; + newdev->errorWeight[cnt] = 0; + newdev->errorRate[cnt] = 0; + newdev->maxErrorRate[cnt] = 0; + } + + cnt = strlen((char *)&buf[buf[0] == 4? 10: 8]); + newdev->bitFileName = malloc(sizeof(char) * (cnt + 1)); + memcpy(newdev->bitFileName, &buf[buf[0] == 4? 10: 8], cnt + 1); + + newdev->usbbus = libusb_get_bus_number(dev); + newdev->usbaddress = libusb_get_device_address(dev); + sprintf(newdev->repr, "ZTEX %.3d:%.3d-%s", newdev->usbbus, newdev->usbaddress, newdev->snString); + newdev->valid = true; + return 0; +} + +void libztex_destroy_device(struct libztex_device* ztex) +{ + if (ztex->hndl != NULL) { + libusb_close(ztex->hndl); + ztex->hndl = NULL; + } + if (ztex->bitFileName != NULL) { + free(ztex->bitFileName); + ztex->bitFileName = NULL; + } + free(ztex); +} + +int libztex_scanDevices(struct libztex_dev_list*** devs_p) +{ + int usbdevices[LIBZTEX_MAX_DESCRIPTORS]; + struct libztex_dev_list **devs; + struct libztex_device *ztex; + int found = 0, pos = 0, err; + libusb_device **list; + ssize_t cnt, i = 0; + + cnt = libusb_get_device_list(NULL, &list); + if (unlikely(cnt < 0)) { + applog(LOG_ERR, "Ztex scan devices: Failed to list usb devices with err %d", cnt); + return 0; + } + + for (i = 0; i < cnt; i++) { + if (libztex_checkDevice(list[i])) { + // Got one! + usbdevices[found] = i; + found++; + } + } + + devs = malloc(sizeof(struct libztex_dev_list *) * found); + if (devs == NULL) { + applog(LOG_ERR, "Ztex scan devices: Failed to allocate memory"); + return 0; + } + + for (i = 0; i < found; i++) { + err = libztex_prepare_device(list[usbdevices[i]], &ztex); + if (unlikely(err != 0)) + applog(LOG_ERR, "prepare device: %d", err); + // check if valid + if (!ztex->valid) { + libztex_destroy_device(ztex); + continue; + } + devs[pos] = malloc(sizeof(struct libztex_dev_list)); + devs[pos]->dev = ztex; + devs[pos]->next = NULL; + if (pos > 0) + devs[pos - 1]->next = devs[pos]; + pos++; + } + + libusb_free_device_list(list, 1); + *devs_p = devs; + return pos; +} + +int libztex_sendHashData(struct libztex_device *ztex, unsigned char *sendbuf) +{ + int cnt; + + if (ztex == NULL || ztex->hndl == NULL) + return 0; + cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x80, 0, 0, sendbuf, 44, 1000); + if (unlikely(cnt < 0)) + applog(LOG_ERR, "%s: Failed sendHashData with err %d", ztex->repr, cnt); + + return cnt; +} + +int libztex_readHashData(struct libztex_device *ztex, struct libztex_hash_data nonces[]) +{ + // length of buf must be 8 * (numNonces + 1) + unsigned char rbuf[12 * 8]; + int cnt, i; + + if (ztex->hndl == NULL) + return 0; + + cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x81, 0, 0, rbuf, 12 * ztex->numNonces, 1000); + if (unlikely(cnt < 0)) { + applog(LOG_ERR, "%s: Failed readHashData with err %d", ztex->repr, cnt); + return cnt; + } + + for (i = 0; i < ztex->numNonces; i++) { + memcpy((char*)&nonces[i].goldenNonce, &rbuf[i * 12], 4); + nonces[i].goldenNonce -= ztex->offsNonces; + memcpy((char*)&nonces[i].nonce, &rbuf[(i * 12) + 4], 4); + nonces[i].nonce -= ztex->offsNonces; + memcpy((char*)&nonces[i].hash7, &rbuf[(i * 12) + 8], 4); + } + + return cnt; +} + +void libztex_freeDevList(struct libztex_dev_list **devs) +{ + bool done = false; + ssize_t cnt = 0; + + while (!done) { + if (devs[cnt]->next == NULL) + done = true; + free(devs[cnt++]); + } + free(devs); +} diff --git a/libztex.h b/libztex.h new file mode 100644 index 00000000..5f88af99 --- /dev/null +++ b/libztex.h @@ -0,0 +1,95 @@ +/** + * libztex.h - headers for Ztex 1.15x fpga board support library + * + * Copyright (c) 2012 nelisky.btc@gmail.com + * + * This work is based upon the Java SDK provided by ztex which is + * Copyright (C) 2009-2011 ZTEX GmbH. + * http://www.ztex.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. +**/ +#ifndef __LIBZTEX_H__ +#define __LIBZTEX_H__ + +#include + +#define LIBZTEX_MAX_DESCRIPTORS 512 +#define LIBZTEX_SNSTRING_LEN 10 + +#define LIBZTEX_IDVENDOR 0x221A +#define LIBZTEX_IDPRODUCT 0x0100 + +#define LIBZTEX_MAXMAXERRORRATE 0.05 +#define LIBZTEX_ERRORHYSTERESIS 0.1 +#define LIBZTEX_OVERHEATTHRESHOLD 0.5 + +struct libztex_fpgastate { + bool fpgaConfigured; + unsigned char fpgaChecksum; + uint16_t fpgaBytes; + unsigned char fpgaInitB; + unsigned char fpgaFlashResult; + bool fpgaFlashBitSwap; +}; + +struct libztex_device { + bool valid; + struct libusb_device_descriptor descriptor; + libusb_device_handle *hndl; + unsigned char usbbus; + unsigned char usbaddress; + unsigned char snString[LIBZTEX_SNSTRING_LEN+1]; + unsigned char productId[4]; + unsigned char fwVersion; + unsigned char interfaceVersion; + unsigned char interfaceCapabilities[6]; + unsigned char moduleReserved[12]; + uint8_t numNonces; + uint16_t offsNonces; + double freqM1; + uint8_t freqM; + uint8_t freqMaxM; + uint8_t freqMDefault; + char* bitFileName; + + double errorCount[256]; + double errorWeight[256]; + double errorRate[256]; + double maxErrorRate[256]; + + char repr[64]; +}; + +struct libztex_dev_list { + struct libztex_device *dev; + struct libztex_dev_list *next; +}; + +struct libztex_hash_data { + uint32_t goldenNonce; + uint32_t nonce; + uint32_t hash7; +}; + +extern int libztex_scanDevices (struct libztex_dev_list ***devs); +extern void libztex_freeDevList (struct libztex_dev_list **devs); +extern int libztex_prepare_device (struct libusb_device *dev, struct libztex_device** ztex); +extern void libztex_destroy_device (struct libztex_device* ztex); +extern int libztex_configureFpga (struct libztex_device *dev); +extern int libztex_setFreq (struct libztex_device *ztex, uint16_t freq); +extern int libztex_sendHashData (struct libztex_device *ztex, unsigned char *sendbuf); +extern int libztex_readHashData (struct libztex_device *ztex, struct libztex_hash_data nonces[]); +extern int libztex_resetFpga (struct libztex_device *ztex); + +#endif /* __LIBZTEX_H__ */ diff --git a/miner.h b/miner.h index cdcb44c9..8cddd513 100644 --- a/miner.h +++ b/miner.h @@ -61,6 +61,14 @@ void *alloca (size_t); #include "ADL_SDK/adl_sdk.h" #endif +#ifdef HAVE_LIBUSB + #include +#endif + +#ifdef USE_ZTEX + #include "libztex.h" +#endif + #if !defined(WIN32) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) #define bswap_16 __builtin_bswap16 #define bswap_32 __builtin_bswap32 @@ -187,6 +195,7 @@ struct thr_info; struct work; struct device_api { + char*dname; char*name; // API-global functions @@ -247,9 +256,15 @@ struct cgpu_info { int cgminer_id; struct device_api *api; int device_id; + char *name; char *device_path; FILE *device_file; - int device_fd; + union { +#ifdef USE_ZTEX + struct libztex_device *device_ztex; +#endif + int device_fd; + }; enum dev_enable deven; int accepted; @@ -270,6 +285,7 @@ struct cgpu_info { int virtual_gpu; int intensity; bool dynamic; + char *kname; #ifdef HAVE_OPENCL cl_uint vwidth; size_t work_size; @@ -308,6 +324,8 @@ struct cgpu_info { int dev_thermal_cutoff_count; }; +extern bool add_cgpu(struct cgpu_info*); + struct thread_q { struct list_head q; @@ -645,6 +663,7 @@ extern int curses_int(const char *query); extern char *curses_input(const char *query); extern void kill_work(void); extern void switch_pools(struct pool *selected); +extern void remove_pool(struct pool *pool); extern void write_config(FILE *fcfg); extern void log_curses(int prio, const char *f, va_list ap); extern void clear_logwin(void); @@ -657,5 +676,6 @@ extern void tq_freeze(struct thread_q *tq); extern void tq_thaw(struct thread_q *tq); extern bool successful_connect; extern void adl(void); +extern void app_restart(void); #endif /* __MINER_H__ */ diff --git a/miner.php b/miner.php index 5cdcc7e4..a8832e40 100644 --- a/miner.php +++ b/miner.php @@ -1,9 +1,11 @@ '; +$tableend = '
'; +$warnfont = ''; +$warnoff = ''; + +# +function htmlhead($checkapi) { global $error, $readonly, $here; - if ($readonly === false) + if ($readonly === false && $checkapi === true) { $access = api('privileged'); if ($error != null @@ -36,17 +53,18 @@ td.h { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#d td.err { color:black; font-family:verdana,arial,sans; font-size:13pt; background:#ff3050 } td.warn { color:black; font-family:verdana,arial,sans; font-size:13pt; background:#ffb050 } td.sta { color:green; font-family:verdana,arial,sans; font-size:13pt; } +td.tot { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#fff8f2 }