diff --git a/API-README b/API-README index b205c90b..c1c4ca44 100644 --- a/API-README +++ b/API-README @@ -347,6 +347,18 @@ The list of requests - a (*) means it requires privileged access - and replies a The current options are: MMQ opt=clock val=160 to 230 (and a multiple of 2) + zero|Which,true/false (*) + none There is no reply section just the STATUS section + stating that the zero, and optional summary, was done + If Which='all', all normal cgminer and API statistics + will be zeroed other than the numbers displayed by the + usbstats and stats commands + If Which='bestshare', only the 'Best Share' values + are zeroed for each pool and the global 'Best Share' + The true/false option determines if a full summary is + shown on the cgminer display like is normally displayed + on exit. + When you enable, disable or restart a GPU or PGA, you will also get Thread messages in the cgminer status window @@ -400,7 +412,18 @@ miner.php - an example web page to access the API Feature Changelog for external applications using the API: -API V1.23 +API V1.24 + +Added API commands: + 'zero' + +Modified API commands: + 'pools' - add 'Best Share' + 'devs' and 'pga' - add 'No Device' for PGAs if MMQ or BFL compiled + +---------- + +API V1.23 (cgminer v2.10.2) Added API commands: 'pgaset' - with: MMQ opt=clock val=160 to 230 (and a multiple of 2) @@ -930,6 +953,38 @@ true --------- +Default: + $userlist = null; + +Define password checking and default access + null means there is no password checking + +$userlist is an array of 3 arrays e.g. +$userlist = array('sys' => array('boss' => 'bpass'), + 'usr' => array('user' => 'upass', 'pleb' => 'ppass'), + 'def' => array('Pools')); + +'sys' is an array of system users and passwords (full access) +'usr' is an array of user level users and passwords (readonly access) +'def' is an array of custompages that anyone not logged in can view + +Any of the 3 can be null, meaning there are none of that item + +All validated 'usr' users are given $readonly = true; access +All validated 'sys' users are given the $readonly access you defined + +If 'def' has one or more values, and allowcustompages is true, then +anyone without a password can see the list of custompage buttons given +in 'def' and will see the first one when they go to the web page, with +a login button at the top right + +From the login page, if you login with no username or password, it will +show the first 'def' custompage (if there are any) + +If you are logged in, it will show a logout button at the top right + +--------- + Default: $notify = true; @@ -1302,9 +1357,12 @@ You can only see fields listed in 'group' and 'calc' A 'calc' is formatted as: 'Field' => 'function' The current list of operations available for 'calc' are: -'sum', 'avg', 'min', 'max', 'lo', 'hi', 'any' +'sum', 'avg', 'min', 'max', 'lo', 'hi', 'coount', 'any' The first 4 are as expected - the numerical sum, average, minimum or maximum 'lo' is the first string of the list, sorted ignoring case 'hi' is the last string of the list, sorted ignoring case +'count' is the number of rows in the section specified in the calc e.g. + ('DEVS.Name' => 'count') would be the number of DEVS selected in the 'where' + of course any valid 'DEVS.Xyz' would give the same 'count' value 'any' is effectively random: the field value in the first row of the grouped data An unrecognised 'function' uses 'any' diff --git a/API.class b/API.class index 026ab141..d506b93d 100644 Binary files a/API.class and b/API.class differ diff --git a/API.java b/API.java index 36c1578a..54dcda99 100644 --- a/API.java +++ b/API.java @@ -76,6 +76,7 @@ class API public void process(String cmd, InetAddress ip, int port) throws Exception { + StringBuffer sb = new StringBuffer(); char buf[] = new char[MAXRECEIVESIZE]; int len = 0; @@ -89,7 +90,15 @@ System.out.println("Attempting to send '"+cmd+"' to "+ip.getHostAddress()+":"+po ps.flush(); InputStreamReader isr = new InputStreamReader(socket.getInputStream()); - len = isr.read(buf, 0, MAXRECEIVESIZE); + while (0x80085 > 0) + { + len = isr.read(buf, 0, MAXRECEIVESIZE); + if (len < 1) + break; + sb.append(buf, 0, len); + if (buf[len-1] == '\0') + break; + } closeAll(); } @@ -100,7 +109,7 @@ System.out.println("Attempting to send '"+cmd+"' to "+ip.getHostAddress()+":"+po return; } - String result = new String(buf, 0, len); + String result = sb.toString(); System.out.println("Answer='"+result+"'"); diff --git a/FPGA-README b/FPGA-README index a970df72..5a1e6a34 100644 --- a/FPGA-README +++ b/FPGA-README @@ -2,15 +2,8 @@ This README contains extended details about FPGA mining with cgminer -ModMinerQuad (MMQ) ------------------- - -The mining bitstream does not survive a power cycle, so cgminer will upload -it, if it needs to, before it starts mining (approx 7min 40sec) - -The red LED also flashes while it is uploading the bitstream - -- +For ModMinerQuad (MMQ) and BitForce (BFL) +----------------------------------------- When mining on windows, the driver being used will determine if mining will work. @@ -39,7 +32,17 @@ problems: --usb-dump 0 -It will only help if you have a working MMQ device attached to the computer +It will only help if you have a working MMQ or BFL device attached to the +computer + + +ModMinerQuad (MMQ) +------------------ + +The mining bitstream does not survive a power cycle, so cgminer will upload +it, if it needs to, before it starts mining (approx 7min 40sec) + +The red LED also flashes while it is uploading the bitstream - @@ -130,7 +133,7 @@ modem-manager software TODO: check that all MMQ's have the same product ID -Bitforce (BFL) +BitForce (BFL) -------------- --bfl-range Use nonce range on bitforce devices if supported diff --git a/Makefile.am b/Makefile.am index 5f846289..b7f90637 100644 --- a/Makefile.am +++ b/Makefile.am @@ -82,6 +82,10 @@ if NEED_FPGAUTILS cgminer_SOURCES += fpgautils.c fpgautils.h endif +if NEED_USBUTILS_C +cgminer_SOURCES += usbutils.c +endif + if HAS_BITFORCE cgminer_SOURCES += driver-bitforce.c endif @@ -91,7 +95,7 @@ cgminer_SOURCES += driver-icarus.c endif if HAS_MODMINER -cgminer_SOURCES += driver-modminer.c usbutils.c +cgminer_SOURCES += driver-modminer.c bitstreamsdir = $(bindir)/bitstreams dist_bitstreams_DATA = bitstreams/* endif diff --git a/README b/README index 44f13855..78293ca8 100644 --- a/README +++ b/README @@ -220,15 +220,16 @@ SCRYPT only options: See SCRYPT-README for more information regarding litecoin mining. -FPGA mining boards(BitForce, Icarus, ModMiner, Ztex) only options: +FPGA mining boards (BitForce, Icarus, ModMiner, Ztex) only options: -cgminer will automatically find your ModMiner or Ztex FPGAs +cgminer will automatically find your ModMiner, BitForce or Ztex FPGAs +independent of the --scan-serial options specified below ---scan-serial|-S Serial port to probe for FPGA mining device +--scan-serial|-S Serial port to probe for Icarus mining device -This option is only for BitForce and/or Icarus FPGAs +This option is only for Icarus bitstream FPGAs -By default, cgminer will scan for autodetected FPGAs unless at least one +By default, cgminer will scan for autodetected Icarus unless at least one -S is specified for that driver. If you specify -S and still want cgminer to scan, you must also use "-S auto". If you want to prevent cgminer from scanning without specifying a device, you can use "-S noauto". Note that @@ -237,13 +238,14 @@ device depending on the version of udev being used. On linux is usually of the format /dev/ttyUSBn On windows is usually of the format \\.\COMn -(where n = the correct device number for the FPGA device) +(where n = the correct device number for the Icarus device) The official supplied binaries are compiled with support for all FPGAs. To force the code to only attempt detection with a specific driver, prepend the argument with the driver name followed by a colon. -For example, "icarus:/dev/ttyUSB0" or "bitforce:\\.\COM5" -or using the short name: "ica:/dev/ttyUSB0" or "bfl:\\.\COM5" +For example, "icarus:/dev/ttyUSB0" or using the short name: "ica:/dev/ttyUSB0" +This option not longer matters since Icarus is the only serial-USB +device that uses it For other FPGA details see the FPGA-README @@ -455,7 +457,7 @@ The block display shows: Block: 0074c5e482e34a506d2a051a... Started: [17:17:22] Best share: 2.71K This shows a short stretch of the current block, when the new block started, -and the all time best difficulty share you've submitted since starting cgminer +and the all time best difficulty share you've found since starting cgminer this time. diff --git a/adl.c b/adl.c index d5e83a90..3ff7e02a 100644 --- a/adl.c +++ b/adl.c @@ -1392,7 +1392,7 @@ updated: clear_logwin(); return; } - sleep(1); + nmsleep(1000); goto updated; } #endif diff --git a/api.c b/api.c index 659d934a..9140df64 100644 --- a/api.c +++ b/api.c @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 Andrew Smith + * Copyright 2011-2013 Andrew Smith * Copyright 2011-2012 Con Kolivas * * This program is free software; you can redistribute it and/or modify it @@ -133,7 +133,7 @@ static const char SEPARATOR = '|'; #define SEPSTR "|" static const char GPUSEP = ','; -static const char *APIVERSION = "1.23"; +static const char *APIVERSION = "1.24"; static const char *DEAD = "Dead"; #if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA) static const char *SICK = "Sick"; @@ -387,6 +387,12 @@ static const char *JSON_PARAMETER = "parameter"; #define MSG_PGASETERR 93 #endif +#define MSG_ZERMIS 94 +#define MSG_ZERINV 95 +#define MSG_ZERSUM 96 +#define MSG_ZERNOSUM 97 +#define MSG_USBNODEV 98 + enum code_severity { SEVERITY_ERR, SEVERITY_WARN, @@ -558,6 +564,13 @@ struct CODES { { 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_ERR, MSG_ZERMIS, PARAM_NONE, "Missing zero parameters" }, + { SEVERITY_ERR, MSG_ZERINV, PARAM_STR, "Invalid zero parameter '%s'" }, + { SEVERITY_SUCC, MSG_ZERSUM, PARAM_STR, "Zeroed %s stats with summary" }, + { SEVERITY_SUCC, MSG_ZERNOSUM, PARAM_STR, "Zeroed %s stats without summary" }, +#if defined(USE_MODMINER) || defined(USE_BITFORCE) + { SEVERITY_ERR, MSG_USBNODEV, PARAM_PGA, "PGA%d has no device" }, #endif { SEVERITY_FAIL, 0, 0, NULL } }; @@ -599,22 +612,6 @@ struct APIGROUPS { static struct IP4ACCESS *ipaccess = NULL; static int ips = 0; -#ifdef USE_BITFORCE -extern struct device_api bitforce_api; -#endif - -#ifdef USE_ICARUS -extern struct device_api icarus_api; -#endif - -#ifdef USE_ZTEX -extern struct device_api ztex_api; -#endif - -#ifdef USE_MODMINER -extern struct device_api modminer_api; -#endif - struct io_data { size_t siz; char *ptr; @@ -1151,24 +1148,26 @@ static int numpgas() int count = 0; int i; + mutex_lock(&devices_lock); for (i = 0; i < total_devices; i++) { #ifdef USE_BITFORCE - if (devices[i]->api == &bitforce_api) + if (devices[i]->drv->drv_id == DRIVER_BITFORCE) count++; #endif #ifdef USE_ICARUS - if (devices[i]->api == &icarus_api) + if (devices[i]->drv->drv_id == DRIVER_ICARUS) count++; #endif #ifdef USE_ZTEX - if (devices[i]->api == &ztex_api) + if (devices[i]->drv->drv_id == DRIVER_ZTEX) count++; #endif #ifdef USE_MODMINER - if (devices[i]->api == &modminer_api) + if (devices[i]->drv->drv_id == DRIVER_MODMINER) count++; #endif } + mutex_unlock(&devices_lock); return count; } @@ -1177,27 +1176,35 @@ static int pgadevice(int pgaid) int count = 0; int i; + mutex_lock(&devices_lock); for (i = 0; i < total_devices; i++) { #ifdef USE_BITFORCE - if (devices[i]->api == &bitforce_api) + if (devices[i]->drv->drv_id == DRIVER_BITFORCE) count++; #endif #ifdef USE_ICARUS - if (devices[i]->api == &icarus_api) + if (devices[i]->drv->drv_id == DRIVER_ICARUS) count++; #endif #ifdef USE_ZTEX - if (devices[i]->api == &ztex_api) + if (devices[i]->drv->drv_id == DRIVER_ZTEX) count++; #endif #ifdef USE_MODMINER - if (devices[i]->api == &modminer_api) + if (devices[i]->drv->drv_id == DRIVER_MODMINER) count++; #endif if (count == (pgaid + 1)) - return i; + goto foundit; } + + mutex_unlock(&devices_lock); return -1; + +foundit: + + mutex_unlock(&devices_lock); + return i; } #endif @@ -1530,16 +1537,16 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom if (dev < 0) // Should never happen return; - struct cgpu_info *cgpu = devices[dev]; + struct cgpu_info *cgpu = get_devices(dev); double frequency = 0; float temp = cgpu->temp; #ifdef USE_ZTEX - if (cgpu->api == &ztex_api && cgpu->device_ztex) + if (cgpu->drv->drv_id == DRIVER_ZTEX && cgpu->device_ztex) frequency = cgpu->device_ztex->freqM1 * (cgpu->device_ztex->freqM + 1); #endif #ifdef USE_MODMINER - if (cgpu->api == &modminer_api) + if (cgpu->drv->drv_id == DRIVER_MODMINER) frequency = cgpu->clock; #endif @@ -1553,7 +1560,7 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom status = (char *)status2str(cgpu->status); root = api_add_int(root, "PGA", &pga, false); - root = api_add_string(root, "Name", cgpu->api->name, false); + root = api_add_string(root, "Name", cgpu->drv->name, false); root = api_add_int(root, "ID", &(cgpu->device_id), false); root = api_add_string(root, "Enabled", enabled, false); root = api_add_string(root, "Status", status, false); @@ -1577,6 +1584,9 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false); root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false); root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false); +#if defined(USE_MODMINER) || defined(USE_BITFORCE) + root = api_add_bool(root, "No Device", &(cgpu->usbinfo.nodev), false); +#endif root = print_data(root, buf, isjson, precom); io_add(io_data, buf); @@ -1747,6 +1757,7 @@ static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *p static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { + struct cgpu_info *cgpu; int numpga = numpgas(); struct thr_info *thr; int pga; @@ -1775,7 +1786,10 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char return; } - struct cgpu_info *cgpu = devices[dev]; + cgpu = get_devices(dev); + + applog(LOG_DEBUG, "API: request to pgaenable pgaid %d device %d %s%u", + id, dev, cgpu->drv->name, cgpu->device_id); if (cgpu->deven != DEV_DISABLED) { message(io_data, MSG_PGALRENA, id, NULL, isjson); @@ -1789,11 +1803,19 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char } #endif +#if defined(USE_MODMINER) || defined(USE_BITFORCE) + if (cgpu->usbinfo.nodev) { + message(io_data, MSG_USBNODEV, id, NULL, isjson); + return; + } +#endif + for (i = 0; i < mining_threads; i++) { - pga = thr_info[i].cgpu->device_id; + thr = get_thread(i); + pga = thr->cgpu->cgminer_id; if (pga == dev) { - thr = &thr_info[i]; cgpu->deven = DEV_ENABLED; + applog(LOG_DEBUG, "API: pushing ping (%d) to thread %d", ping, thr->id); tq_push(thr->q, &ping); } } @@ -1803,6 +1825,7 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { + struct cgpu_info *cgpu; int numpga = numpgas(); int id; @@ -1828,7 +1851,10 @@ static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha return; } - struct cgpu_info *cgpu = devices[dev]; + cgpu = get_devices(dev); + + applog(LOG_DEBUG, "API: request to pgadisable pgaid %d device %d %s%u", + id, dev, cgpu->drv->name, cgpu->device_id); if (cgpu->deven == DEV_DISABLED) { message(io_data, MSG_PGALRDIS, id, NULL, isjson); @@ -1842,6 +1868,8 @@ static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha static void pgaidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { + struct cgpu_info *cgpu; + struct device_drv *drv; int numpga = numpgas(); int id; @@ -1867,13 +1895,13 @@ static void pgaidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, ch return; } - struct cgpu_info *cgpu = devices[dev]; - struct device_api *api = cgpu->api; + cgpu = get_devices(dev); + drv = cgpu->drv; - if (!api->identify_device) + if (!drv->identify_device) message(io_data, MSG_PGANOID, id, NULL, isjson); else { - api->identify_device(cgpu); + drv->identify_device(cgpu); message(io_data, MSG_PGAIDENT, id, NULL, isjson); } } @@ -1993,6 +2021,7 @@ static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m else root = api_add_const(root, "Stratum URL", BLANK, false); root = api_add_bool(root, "Has GBT", &(pool->has_gbt), false); + root = api_add_uint64(root, "Best Share", &(pool->best_diff), true); root = print_data(root, buf, isjson, isjson && (i > 0)); io_add(io_data, buf); @@ -2081,23 +2110,25 @@ static void gpuenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char return; } + applog(LOG_DEBUG, "API: request to gpuenable gpuid %d %s%u", + id, gpus[id].drv->name, gpus[id].device_id); + if (gpus[id].deven != DEV_DISABLED) { message(io_data, MSG_ALRENA, id, NULL, isjson); return; } for (i = 0; i < gpu_threads; i++) { - gpu = thr_info[i].cgpu->device_id; + thr = get_thread(i); + gpu = thr->cgpu->device_id; if (gpu == id) { - thr = &thr_info[i]; if (thr->cgpu->status != LIFE_WELL) { message(io_data, MSG_GPUMRE, id, NULL, isjson); return; } - gpus[id].deven = DEV_ENABLED; + applog(LOG_DEBUG, "API: pushing ping (%d) to thread %d", ping, thr->id); tq_push(thr->q, &ping); - } } @@ -2124,6 +2155,9 @@ static void gpudisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha return; } + applog(LOG_DEBUG, "API: request to gpudisable gpuid %d %s%u", + id, gpus[id].drv->name, gpus[id].device_id); + if (gpus[id].deven == DEV_DISABLED) { message(io_data, MSG_ALRDIS, id, NULL, isjson); return; @@ -2752,7 +2786,7 @@ void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, b // ALL counters (and only counters) must start the name with a '*' // Simplifies future external support for identifying new counters root = api_add_int(root, "NOTIFY", &device, false); - root = api_add_string(root, "Name", cgpu->api->name, false); + root = api_add_string(root, "Name", cgpu->drv->name, false); root = api_add_int(root, "ID", &(cgpu->device_id), false); root = api_add_time(root, "Last Well", &(cgpu->device_last_well), false); root = api_add_time(root, "Last Not Well", &(cgpu->device_last_not_well), false); @@ -2774,6 +2808,7 @@ void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, b static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, char group) { + struct cgpu_info *cgpu; bool io_open = false; int i; @@ -2787,8 +2822,10 @@ static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe if (isjson) io_open = io_add(io_data, COMSTR JSON_NOTIFY); - for (i = 0; i < total_devices; i++) - notifystatus(io_data, i, devices[i], isjson, group); + for (i = 0; i < total_devices; i++) { + cgpu = get_devices(i); + notifystatus(io_data, i, cgpu, isjson, group); + } if (isjson && io_open) io_close(io_data); @@ -2813,12 +2850,12 @@ static void devdetails(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m io_open = io_add(io_data, COMSTR JSON_DEVDETAILS); for (i = 0; i < total_devices; i++) { - cgpu = devices[i]; + cgpu = get_devices(i); root = api_add_int(root, "DEVDETAILS", &i, false); - root = api_add_string(root, "Name", cgpu->api->name, false); + root = api_add_string(root, "Name", cgpu->drv->name, false); root = api_add_int(root, "ID", &(cgpu->device_id), false); - root = api_add_string(root, "Driver", cgpu->api->dname, false); + root = api_add_string(root, "Driver", cgpu->drv->dname, false); root = api_add_const(root, "Kernel", cgpu->kname ? : BLANK, false); root = api_add_const(root, "Model", cgpu->name ? : BLANK, false); root = api_add_const(root, "Device Path", cgpu->device_path ? : BLANK, false); @@ -2895,6 +2932,8 @@ static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_st root = api_add_uint64(root, "Bytes Sent", &(pool_stats->bytes_sent), false); root = api_add_uint64(root, "Times Recv", &(pool_stats->times_received), false); root = api_add_uint64(root, "Bytes Recv", &(pool_stats->bytes_received), false); + root = api_add_uint64(root, "Net Bytes Sent", &(pool_stats->net_bytes_sent), false); + root = api_add_uint64(root, "Net Bytes Recv", &(pool_stats->net_bytes_received), false); } if (extra) @@ -2908,6 +2947,7 @@ static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_st static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { + struct cgpu_info *cgpu; bool io_open = false; struct api_data *extra; char id[20]; @@ -2920,15 +2960,15 @@ static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m i = 0; for (j = 0; j < total_devices; j++) { - struct cgpu_info *cgpu = devices[j]; + cgpu = get_devices(j); - if (cgpu && cgpu->api) { - if (cgpu->api->get_api_stats) - extra = cgpu->api->get_api_stats(cgpu); + if (cgpu && cgpu->drv) { + if (cgpu->drv->get_api_stats) + extra = cgpu->drv->get_api_stats(cgpu); else extra = NULL; - sprintf(id, "%s%d", cgpu->api->name, cgpu->device_id); + sprintf(id, "%s%d", cgpu->drv->name, cgpu->device_id); i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, isjson); } } @@ -3120,7 +3160,7 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may { struct api_data *root = NULL; -#ifdef USE_MODMINER +#if defined(USE_MODMINER) || defined(USE_BITFORCE) char buf[TMPBUFSIZ]; bool io_open = false; int count = 0; @@ -3133,7 +3173,7 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may return; } -#ifdef USE_MODMINER +#if defined(USE_MODMINER) || defined(USE_BITFORCE) message(io_data, MSG_USBSTA, 0, NULL, isjson); @@ -3160,6 +3200,8 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may #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) { + struct cgpu_info *cgpu; + struct device_drv *drv; char buf[TMPBUFSIZ]; int numpga = numpgas(); @@ -3193,17 +3235,17 @@ static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe return; } - struct cgpu_info *cgpu = devices[dev]; - struct device_api *api = cgpu->api; + cgpu = get_devices(dev); + drv = cgpu->drv; char *set = strchr(opt, ','); if (set) *(set++) = '\0'; - if (!api->set_device) + if (!drv->set_device) message(io_data, MSG_PGANOSET, id, NULL, isjson); else { - char *ret = api->set_device(cgpu, opt, set, buf); + char *ret = drv->set_device(cgpu, opt, set, buf); if (ret) { if (strcasecmp(opt, "help") == 0) message(io_data, MSG_PGAHELP, id, ret, isjson); @@ -3215,6 +3257,54 @@ static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe } #endif +static void dozero(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) +{ + if (param == NULL || *param == '\0') { + message(io_data, MSG_ZERMIS, 0, NULL, isjson); + return; + } + + char *sum = strchr(param, ','); + if (sum) + *(sum++) = '\0'; + if (!sum || !*sum) { + message(io_data, MSG_MISBOOL, 0, NULL, isjson); + return; + } + + bool all = false; + bool bs = false; + if (strcasecmp(param, "all") == 0) + all = true; + else if (strcasecmp(param, "bestshare") == 0) + bs = true; + + if (all == false && bs == false) { + message(io_data, MSG_ZERINV, 0, param, isjson); + return; + } + + *sum = tolower(*sum); + if (*sum != 't' && *sum != 'f') { + message(io_data, MSG_INVBOOL, 0, NULL, isjson); + return; + } + + bool dosum = (*sum == 't'); + if (dosum) + print_summary(); + + if (all) + zero_stats(); + if (bs) + zero_bestshare(); + + if (dosum) + message(io_data, MSG_ZERSUM, 0, all ? "All" : "BestShare", isjson); + else + message(io_data, MSG_ZERNOSUM, 0, all ? "All" : "BestShare", isjson); +} + static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group); struct CMDS { @@ -3274,6 +3364,7 @@ struct CMDS { #ifdef HAVE_AN_FPGA { "pgaset", pgaset, true }, #endif + { "zero", dozero, true }, { NULL, NULL, false } }; @@ -3342,12 +3433,10 @@ static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson) // ignore failure - it's closed immediately anyway n = send(c, buf, len+1, 0); - if (opt_debug) { - if (SOCKETFAIL(n)) - applog(LOG_DEBUG, "API: send failed: %s", SOCKERRMSG); - else - applog(LOG_DEBUG, "API: sent %d", n); - } + if (SOCKETFAIL(n)) + applog(LOG_WARNING, "API: send failed: %s", SOCKERRMSG); + else + applog(LOG_DEBUG, "API: sent %d", n); } static void tidyup(__maybe_unused void *arg) @@ -3701,7 +3790,7 @@ void api(int api_thr_id) /* This should be done before curl in needed * to ensure curl has already called WSAStartup() in windows */ - sleep(opt_log_interval); + nmsleep(opt_log_interval*1000); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVSOCK) { @@ -3747,7 +3836,7 @@ void api(int api_thr_id) break; else { applog(LOG_WARNING, "API bind to port %d failed - trying again in 30sec", port); - sleep(30); + nmsleep(30000); } } else bound = 1; diff --git a/cgminer.c b/cgminer.c index 86e9c29f..1caac24b 100644 --- a/cgminer.c +++ b/cgminer.c @@ -146,7 +146,8 @@ bool opt_bfl_noncerange; #endif #define QUIET (opt_quiet || opt_realquiet) -struct thr_info *thr_info; +struct thr_info *control_thr; +struct thr_info **mining_thr; static int gwsched_thr_id; static int stage_thr_id; static int watchpool_thr_id; @@ -156,7 +157,14 @@ static int input_thr_id; #endif int gpur_thr_id; static int api_thr_id; -static int total_threads; +#if defined(USE_MODMINER) || defined(USE_BITFORCE) +static int hotplug_thr_id; +#endif +static int total_control_threads; +bool hotplug_mode; +static int new_devices; +static int new_threads; +static int start_devices; #ifdef HAVE_LIBUSB pthread_mutex_t cgusb_lock; @@ -171,6 +179,8 @@ static pthread_rwlock_t blk_lock; static pthread_mutex_t sshare_lock; pthread_rwlock_t netacc_lock; +pthread_mutex_t mining_thr_lock; +pthread_mutex_t devices_lock; static pthread_mutex_t lp_lock; static pthread_cond_t lp_cond; @@ -364,6 +374,33 @@ static void applog_and_exit(const char *fmt, ...) static pthread_mutex_t sharelog_lock; static FILE *sharelog_file = NULL; +struct thr_info *get_thread(int thr_id) +{ + struct thr_info *thr; + + mutex_lock(&mining_thr_lock); + thr = mining_thr[thr_id]; + mutex_unlock(&mining_thr_lock); + return thr; +} + +static struct cgpu_info *get_thr_cgpu(int thr_id) +{ + struct thr_info *thr = get_thread(thr_id); + + return thr->cgpu; +} + +struct cgpu_info *get_devices(int id) +{ + struct cgpu_info *cgpu; + + mutex_lock(&devices_lock); + cgpu = devices[id]; + mutex_unlock(&devices_lock); + return cgpu; +} + static void sharelog(const char*disposition, const struct work*work) { char *target, *hash, *data; @@ -378,7 +415,7 @@ static void sharelog(const char*disposition, const struct work*work) return; thr_id = work->thr_id; - cgpu = thr_info[thr_id].cgpu; + cgpu = get_thr_cgpu(thr_id); pool = work->pool; t = (unsigned long int)(work->tv_work_found.tv_sec); target = bin2hex(work->target, sizeof(work->target)); @@ -386,7 +423,7 @@ static void sharelog(const char*disposition, const struct work*work) data = bin2hex(work->data, sizeof(work->data)); // timestamp,disposition,target,pool,dev,thr,sharehash,sharedata - rv = snprintf(s, sizeof(s), "%lu,%s,%s,%s,%s%u,%u,%s,%s\n", t, disposition, target, pool->rpc_url, cgpu->api->name, cgpu->device_id, thr_id, hash, data); + rv = snprintf(s, sizeof(s), "%lu,%s,%s,%s,%s%u,%u,%s,%s\n", t, disposition, target, pool->rpc_url, cgpu->drv->name, cgpu->device_id, thr_id, hash, data); free(target); free(hash); free(data); @@ -724,18 +761,24 @@ static void load_temp_cutoffs() if (val < 0 || val > 200) quit(1, "Invalid value passed to set temp cutoff"); + mutex_lock(&devices_lock); devices[device]->cutofftemp = val; + mutex_unlock(&devices_lock); } } else { + mutex_lock(&devices_lock); for (i = device; i < total_devices; ++i) { if (!devices[i]->cutofftemp) devices[i]->cutofftemp = opt_cutofftemp; } + mutex_unlock(&devices_lock); return; } if (device <= 1) { + mutex_lock(&devices_lock); for (i = device; i < total_devices; ++i) devices[i]->cutofftemp = val; + mutex_unlock(&devices_lock); } } @@ -1723,7 +1766,9 @@ out: int dev_from_id(int thr_id) { - return thr_info[thr_id].cgpu->device_id; + struct cgpu_info *cgpu = get_thr_cgpu(thr_id); + + return cgpu->device_id; } /* Make the change in the recent value adjust dynamically when the difference @@ -1876,11 +1921,8 @@ static void get_statline(char *buf, struct cgpu_info *cgpu) suffix_string(dh64, displayed_hashes, 4); suffix_string(dr64, displayed_rolling, 4); - 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, " | "); + sprintf(buf, "%s%d ", cgpu->drv->name, cgpu->device_id); + cgpu->drv->get_statline_before(buf, cgpu); tailsprintf(buf, "(%ds):%s (avg):%sh/s | A:%d R:%d HW:%d U:%.1f/m", opt_log_interval, displayed_rolling, @@ -1889,15 +1931,15 @@ static void get_statline(char *buf, struct cgpu_info *cgpu) cgpu->rejected, cgpu->hw_errors, cgpu->utility); - if (cgpu->api->get_statline) - cgpu->api->get_statline(buf, cgpu); + cgpu->drv->get_statline(buf, cgpu); } static void text_print_status(int thr_id) { - struct cgpu_info *cgpu = thr_info[thr_id].cgpu; + struct cgpu_info *cgpu; char logline[256]; + cgpu = get_thr_cgpu(thr_id); if (cgpu) { get_statline(logline, cgpu); printf("%s\n", logline); @@ -1961,37 +2003,43 @@ static int dev_width; static void curses_print_devstatus(int thr_id) { static int awidth = 1, rwidth = 1, hwwidth = 1, uwidth = 1; - struct cgpu_info *cgpu = thr_info[thr_id].cgpu; + struct cgpu_info *cgpu; char logline[256]; char displayed_hashes[16], displayed_rolling[16]; uint64_t dh64, dr64; - if (devcursor + cgpu->cgminer_id > LINES - 2 || opt_compact) + if (opt_compact) + return; + + cgpu = get_thr_cgpu(thr_id); + + if (cgpu->cgminer_id >= start_devices || devcursor + cgpu->cgminer_id > LINES - 2) return; cgpu->utility = cgpu->accepted / total_secs * 60; wmove(statuswin,devcursor + cgpu->cgminer_id, 0); - wprintw(statuswin, " %s %*d: ", cgpu->api->name, dev_width, cgpu->device_id); - if (cgpu->api->get_statline_before) { - logline[0] = '\0'; - cgpu->api->get_statline_before(logline, cgpu); - wprintw(statuswin, "%s", logline); - } - else - wprintw(statuswin, " | "); + wprintw(statuswin, " %s %*d: ", cgpu->drv->name, dev_width, cgpu->device_id); + logline[0] = '\0'; + cgpu->drv->get_statline_before(logline, cgpu); + wprintw(statuswin, "%s", logline); dh64 = (double)cgpu->total_mhashes / total_secs * 1000000ull; dr64 = (double)cgpu->rolling * 1000000ull; suffix_string(dh64, displayed_hashes, 4); suffix_string(dr64, displayed_rolling, 4); +#if defined(USE_MODMINER) || defined(USE_BITFORCE) + if (cgpu->usbinfo.nodev) + wprintw(statuswin, "ZOMBIE"); + else +#endif if (cgpu->status == LIFE_DEAD) - wprintw(statuswin, "DEAD "); + wprintw(statuswin, "DEAD "); else if (cgpu->status == LIFE_SICK) - wprintw(statuswin, "SICK "); + wprintw(statuswin, "SICK "); else if (cgpu->deven == DEV_DISABLED) - wprintw(statuswin, "OFF "); + wprintw(statuswin, "OFF "); else if (cgpu->deven == DEV_RECOVER) wprintw(statuswin, "REST "); else @@ -2008,11 +2056,9 @@ static void curses_print_devstatus(int thr_id) hwwidth, cgpu->hw_errors, uwidth + 3, cgpu->utility); - if (cgpu->api->get_statline) { - logline[0] = '\0'; - cgpu->api->get_statline(logline, cgpu); - wprintw(statuswin, "%s", logline); - } + logline[0] = '\0'; + cgpu->drv->get_statline(logline, cgpu); + wprintw(statuswin, "%s", logline); wclrtoeol(statuswin); } @@ -2217,7 +2263,9 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work, char *hashshow, bool resubmit, char *worktime) { struct pool *pool = work->pool; - struct cgpu_info *cgpu = thr_info[work->thr_id].cgpu; + struct cgpu_info *cgpu; + + cgpu = get_thr_cgpu(work->thr_id); if (json_is_true(res) || (work->gbt && json_is_null(res))) { mutex_lock(&stats_lock); @@ -2239,10 +2287,10 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work, if (!QUIET) { if (total_pools > 1) applog(LOG_NOTICE, "Accepted %s %s %d pool %d %s%s", - hashshow, cgpu->api->name, cgpu->device_id, work->pool->pool_no, resubmit ? "(resubmit)" : "", worktime); + hashshow, cgpu->drv->name, cgpu->device_id, work->pool->pool_no, resubmit ? "(resubmit)" : "", worktime); else applog(LOG_NOTICE, "Accepted %s %s %d %s%s", - hashshow, cgpu->api->name, cgpu->device_id, resubmit ? "(resubmit)" : "", worktime); + hashshow, cgpu->drv->name, cgpu->device_id, resubmit ? "(resubmit)" : "", worktime); } sharelog("accept", work); if (opt_shares && total_accepted >= opt_shares) { @@ -2307,7 +2355,7 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work, } applog(LOG_NOTICE, "Rejected %s %s %d %s%s %s%s", - hashshow, cgpu->api->name, cgpu->device_id, where, reason, resubmit ? "(resubmit)" : "", worktime); + hashshow, cgpu->drv->name, cgpu->device_id, where, reason, resubmit ? "(resubmit)" : "", worktime); sharelog(disposition, work); } @@ -2354,6 +2402,8 @@ static uint64_t share_diff(const struct work *work) best_diff = ret; suffix_string(best_diff, best_share, 0); } + if (ret > work->pool->best_diff) + work->pool->best_diff = ret; mutex_unlock(&control_lock); return ret; } @@ -2365,13 +2415,15 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit) char *s; bool rc = false; int thr_id = work->thr_id; - struct cgpu_info *cgpu = thr_info[thr_id].cgpu; + struct cgpu_info *cgpu; struct pool *pool = work->pool; int rolltime; struct timeval tv_submit, tv_submit_reply; char hashshow[64 + 4] = ""; char worktime[200] = ""; + cgpu = get_thr_cgpu(thr_id); + #ifdef __BIG_ENDIAN__ int swapcounter = 0; for (swapcounter = 0; swapcounter < 32; swapcounter++) @@ -2438,7 +2490,7 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit) pool->remotefail_occasions++; applog(LOG_WARNING, "Pool %d communication failure, caching submissions", pool->pool_no); } - sleep(5); + nmsleep(5000); goto out; } else if (pool_tclear(pool, &pool->submit_fail)) applog(LOG_WARNING, "Pool %d communication resumed, submitting work", pool->pool_no); @@ -2579,7 +2631,7 @@ static inline struct pool *select_pool(bool lagging) return pool; } -static double DIFFEXACTONE = 26959946667150639794667015087019630673637144422540572481103610249216.0; +static double DIFFEXACTONE = 26959946667150639794667015087019630673637144422540572481103610249215.0; /* * Calculate the work share difficulty @@ -2735,8 +2787,6 @@ static void disable_curses(void) } #endif -static void print_summary(void); - static void __kill_work(void) { struct thr_info *thr; @@ -2747,40 +2797,50 @@ static void __kill_work(void) applog(LOG_INFO, "Received kill message"); +#if defined(USE_MODMINER) || defined(USE_BITFORCE) + /* Best to get rid of it first so it doesn't + * try to create any new devices */ + if (!opt_scrypt) { + applog(LOG_DEBUG, "Killing off HotPlug thread"); + thr = &control_thr[hotplug_thr_id]; + thr_info_cancel(thr); + } +#endif + applog(LOG_DEBUG, "Killing off watchpool thread"); /* Kill the watchpool thread */ - thr = &thr_info[watchpool_thr_id]; + thr = &control_thr[watchpool_thr_id]; thr_info_cancel(thr); applog(LOG_DEBUG, "Killing off watchdog thread"); /* Kill the watchdog thread */ - thr = &thr_info[watchdog_thr_id]; + thr = &control_thr[watchdog_thr_id]; thr_info_cancel(thr); applog(LOG_DEBUG, "Stopping mining threads"); /* Stop the mining threads*/ for (i = 0; i < mining_threads; i++) { - thr = &thr_info[i]; + thr = get_thread(i); thr_info_freeze(thr); thr->pause = true; } - sleep(1); + nmsleep(1000); applog(LOG_DEBUG, "Killing off mining threads"); /* Kill the mining threads*/ for (i = 0; i < mining_threads; i++) { - thr = &thr_info[i]; + thr = get_thread(i); thr_info_cancel(thr); } applog(LOG_DEBUG, "Killing off stage thread"); /* Stop the others */ - thr = &thr_info[stage_thr_id]; + thr = &control_thr[stage_thr_id]; thr_info_cancel(thr); applog(LOG_DEBUG, "Killing off API thread"); - thr = &thr_info[api_thr_id]; + thr = &control_thr[api_thr_id]; thr_info_cancel(thr); } @@ -3417,8 +3477,10 @@ static void restart_threads(void) /* Discard staged work that is now stale */ discard_stale(); + mutex_lock(&mining_thr_lock); for (i = 0; i < mining_threads; i++) - thr_info[i].work_restart = true; + mining_thr[i]->work_restart = true; + mutex_unlock(&mining_thr_lock); mutex_lock(&restart_lock); pthread_cond_broadcast(&restart_cond); @@ -3966,6 +4028,20 @@ void write_config(FILE *fcfg) json_escape_free(); } +void zero_bestshare(void) +{ + int i; + + best_diff = 0; + memset(best_share, 0, 8); + suffix_string(best_diff, best_share, 0); + + for (i = 0; i < total_pools; i++) { + struct pool *pool = pools[i]; + pool->best_diff = 0; + } +} + void zero_stats(void) { int i; @@ -3982,10 +4058,11 @@ void zero_stats(void) total_go = 0; total_ro = 0; total_secs = 1.0; - best_diff = 0; total_diff1 = 0; - memset(best_share, 0, 8); - suffix_string(best_diff, best_share, 0); + found_blocks = 0; + total_diff_accepted = 0; + total_diff_rejected = 0; + total_diff_stale = 0; for (i = 0; i < total_pools; i++) { struct pool *pool = pools[i]; @@ -3997,19 +4074,32 @@ void zero_stats(void) pool->discarded_work = 0; pool->getfail_occasions = 0; pool->remotefail_occasions = 0; + pool->last_share_time = 0; + pool->diff1 = 0; + pool->diff_accepted = 0; + pool->diff_rejected = 0; + pool->diff_stale = 0; + pool->last_share_diff = 0; } - mutex_lock(&hash_lock); + zero_bestshare(); + for (i = 0; i < total_devices; ++i) { - struct cgpu_info *cgpu = devices[i]; + struct cgpu_info *cgpu = get_devices(i); + mutex_lock(&hash_lock); cgpu->total_mhashes = 0; cgpu->accepted = 0; cgpu->rejected = 0; cgpu->hw_errors = 0; cgpu->utility = 0.0; + cgpu->last_share_pool_time = 0; + cgpu->diff1 = 0; + cgpu->diff_accepted = 0; + cgpu->diff_rejected = 0; + cgpu->last_share_diff = 0; + mutex_unlock(&hash_lock); } - mutex_unlock(&hash_lock); } #ifdef HAVE_CURSES @@ -4433,20 +4523,21 @@ static void hashmeter(int thr_id, struct timeval *diff, bool showlog = false; char displayed_hashes[16], displayed_rolling[16]; uint64_t dh64, dr64; + struct thr_info *thr; local_mhashes = (double)hashes_done / 1000000.0; /* Update the last time this thread reported in */ if (thr_id >= 0) { - gettimeofday(&thr_info[thr_id].last, NULL); - thr_info[thr_id].cgpu->device_last_well = time(NULL); + thr = get_thread(thr_id); + gettimeofday(&(thr->last), NULL); + thr->cgpu->device_last_well = time(NULL); } secs = (double)diff->tv_sec + ((double)diff->tv_usec / 1000000.0); /* So we can call hashmeter from a non worker thread */ if (thr_id >= 0) { - struct thr_info *thr = &thr_info[thr_id]; - struct cgpu_info *cgpu = thr_info[thr_id].cgpu; + struct cgpu_info *cgpu = thr->cgpu; double thread_rolling = 0.0; int i; @@ -4614,12 +4705,14 @@ out: void clear_stratum_shares(struct pool *pool) { struct stratum_share *sshare, *tmpshare; + double diff_cleared = 0; int cleared = 0; mutex_lock(&sshare_lock); HASH_ITER(hh, stratum_shares, sshare, tmpshare) { if (sshare->work->pool == pool) { HASH_DEL(stratum_shares, sshare); + diff_cleared += sshare->work->work_difficulty; free_work(sshare->work); free(sshare); cleared++; @@ -4631,6 +4724,8 @@ void clear_stratum_shares(struct pool *pool) applog(LOG_WARNING, "Lost %d shares due to stratum disconnect on pool %d", cleared, pool->pool_no); pool->stale_shares += cleared; total_stale += cleared; + pool->diff_stale += diff_cleared; + total_diff_stale += diff_cleared; } } @@ -4738,7 +4833,7 @@ static void *stratum_thread(void *userdata) while (!restart_stratum(pool)) { if (pool->removed) goto out; - sleep(30); + nmsleep(30000); } } } @@ -4777,7 +4872,7 @@ static void *stratum_thread(void *userdata) while (!restart_stratum(pool)) { if (pool->removed) goto out; - sleep(30); + nmsleep(30000); } stratum_resumed(pool); continue; @@ -4952,7 +5047,7 @@ retry_stratum: calc_diff(work, 0); applog(LOG_DEBUG, "Pushing pooltest work to base pool"); - tq_push(thr_info[stage_thr_id].q, work); + tq_push(control_thr[stage_thr_id].q, work); total_getworks++; pool->getwork_requested++; ret = true; @@ -5299,15 +5394,14 @@ static bool hashtest(struct thr_info *thr, struct work *work) if (hash2_32[7] != 0) { applog(LOG_WARNING, "%s%d: invalid nonce - HW error", - thr->cgpu->api->name, thr->cgpu->device_id); + thr->cgpu->drv->name, thr->cgpu->device_id); mutex_lock(&stats_lock); hw_errors++; thr->cgpu->hw_errors++; mutex_unlock(&stats_lock); - if (thr->cgpu->api->hw_error) - thr->cgpu->api->hw_error(thr); + thr->cgpu->drv->hw_error(thr); goto out; } @@ -5355,7 +5449,7 @@ static inline bool abandon_work(struct work *work, struct timeval *wdiff, uint64 } static void mt_disable(struct thr_info *mythr, const int thr_id, - struct device_api *api) + struct device_drv *drv) { applog(LOG_WARNING, "Thread %d being disabled", thr_id); mythr->rolling = mythr->cgpu->rolling = 0; @@ -5366,59 +5460,42 @@ static void mt_disable(struct thr_info *mythr, const int thr_id, } while (mythr->pause); thread_reportin(mythr); applog(LOG_WARNING, "Thread %d being re-enabled", thr_id); - if (api->thread_enable) - api->thread_enable(mythr); + drv->thread_enable(mythr); } -void *miner_thread(void *userdata) +/* The main hashing loop for devices that are slow enough to work on one work + * item at a time, without a queue, aborting work before the entire nonce + * range has been hashed if needed. */ +static void hash_sole_work(struct thr_info *mythr) { - struct thr_info *mythr = userdata; const int thr_id = mythr->id; struct cgpu_info *cgpu = mythr->cgpu; - struct device_api *api = cgpu->api; + struct device_drv *drv = cgpu->drv; + struct timeval getwork_start, tv_start, tv_end, tv_workstart, tv_lastupdate; struct cgminer_stats *dev_stats = &(cgpu->cgminer_stats); struct cgminer_stats *pool_stats; - struct timeval getwork_start; - /* Try to cycle approximately 5 times before each log update */ const long cycle = opt_log_interval / 5 ? : 1; - struct timeval tv_start, tv_end, tv_workstart, tv_lastupdate; + const bool primary = (!mythr->device_thread) || mythr->primary_thread; struct timeval diff, sdiff, wdiff = {0, 0}; - uint32_t max_nonce = api->can_limit_work ? api->can_limit_work(mythr) : 0xffffffff; + uint32_t max_nonce = drv->can_limit_work(mythr); int64_t hashes_done = 0; - int64_t hashes; - struct work *work; - const bool primary = (!mythr->device_thread) || mythr->primary_thread; - - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - - char threadname[20]; - snprintf(threadname,20,"miner %d",thr_id); - RenameThread(threadname); gettimeofday(&getwork_start, NULL); - - if (api->thread_init && !api->thread_init(mythr)) { - dev_error(cgpu, REASON_THREAD_FAIL_INIT); - goto out; - } - - thread_reportout(mythr); - applog(LOG_DEBUG, "Popping ping in miner thread"); - tq_pop(mythr->q, NULL); /* Wait for a ping to start */ - sdiff.tv_sec = sdiff.tv_usec = 0; gettimeofday(&tv_lastupdate, NULL); - while (1) { + while (42) { + struct work *work = get_work(mythr, thr_id); + int64_t hashes; + mythr->work_restart = false; - work = get_work(mythr, thr_id); cgpu->new_work = true; gettimeofday(&tv_workstart, NULL); work->blk.nonce = 0; cgpu->max_hashes = 0; - if (api->prepare_work && !api->prepare_work(mythr, work)) { + if (!drv->prepare_work(mythr, work)) { applog(LOG_ERR, "work prepare failed, exiting " "mining thread %d", thr_id); break; @@ -5460,16 +5537,16 @@ void *miner_thread(void *userdata) gettimeofday(&(work->tv_work_start), NULL); thread_reportin(mythr); - hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce); + hashes = drv->scanhash(mythr, work, work->blk.nonce + max_nonce); thread_reportin(mythr); gettimeofday(&getwork_start, NULL); if (unlikely(hashes == -1)) { - applog(LOG_ERR, "%s %d failure, disabling!", api->name, cgpu->device_id); + applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id); cgpu->deven = DEV_DISABLED; dev_error(cgpu, REASON_THREAD_ZERO_HASH); - mt_disable(mythr, thr_id, api); + mt_disable(mythr, thr_id, drv); } hashes_done += hashes; @@ -5490,7 +5567,7 @@ void *miner_thread(void *userdata) if (unlikely((long)sdiff.tv_sec < cycle)) { int mult; - if (likely(!api->can_limit_work || max_nonce == 0xffffffff)) + if (likely(max_nonce == 0xffffffff)) continue; mult = 1000000 / ((sdiff.tv_usec + 0x400) / 0x400) + 0x10; @@ -5499,9 +5576,9 @@ void *miner_thread(void *userdata) max_nonce = 0xffffffff; else max_nonce = (max_nonce * mult) / 0x400; - } else if (unlikely(sdiff.tv_sec > cycle) && api->can_limit_work) + } else if (unlikely(sdiff.tv_sec > cycle)) max_nonce = max_nonce * cycle / sdiff.tv_sec; - else if (unlikely(sdiff.tv_usec > 100000) && api->can_limit_work) + else if (unlikely(sdiff.tv_usec > 100000)) max_nonce = max_nonce * 0x400 / (((cycle * 1000000) + sdiff.tv_usec) / (cycle * 1000000 / 0x400)); timersub(&tv_end, &tv_lastupdate, &diff); @@ -5527,16 +5604,162 @@ void *miner_thread(void *userdata) } if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED)) - mt_disable(mythr, thr_id, api); + mt_disable(mythr, thr_id, drv); sdiff.tv_sec = sdiff.tv_usec = 0; } while (!abandon_work(work, &wdiff, cgpu->max_hashes)); free_work(work); } +} +/* Create a hashtable of work items for devices with a queue. The device + * driver must have a custom queue_full function or it will default to true + * and put only one work item in the queue. Work items should not be removed + * from this hashtable until they are no longer in use anywhere. Once a work + * item is physically queued on the device itself, the work->queued flag + * should be set under cgpu->qlock write lock to prevent it being dereferenced + * while still in use. */ +static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct device_drv *drv, const int thr_id) +{ + thread_reportout(mythr); + do { + struct work *work = get_work(mythr, thr_id); + + wr_lock(&cgpu->qlock); + HASH_ADD_INT(cgpu->queued_work, id, work); + wr_unlock(&cgpu->qlock); + /* The queue_full function should be used by the driver to + * actually place work items on the physical device if it + * does have a queue. */ + } while (!drv->queue_full(cgpu)); +} + +/* This function is for retrieving one work item from the queued hashtable of + * available work items that are not yet physically on a device (which is + * flagged with the work->queued bool). Code using this function must be able + * to handle NULL as a return which implies there is no work available. */ +struct work *get_queued(struct cgpu_info *cgpu) +{ + struct work *work, *tmp, *ret = NULL; + + wr_lock(&cgpu->qlock); + HASH_ITER(hh, cgpu->queued_work, work, tmp) { + if (!work->queued) { + work->queued = true; + ret = work; + break; + } + } + wr_unlock(&cgpu->qlock); + + return ret; +} + +/* This function should be used by queued device drivers when they're sure + * the work struct is no longer in use. */ +void work_completed(struct cgpu_info *cgpu, struct work *work) +{ + wr_lock(&cgpu->qlock); + HASH_DEL(cgpu->queued_work, work); + wr_unlock(&cgpu->qlock); + free_work(work); +} + +static void flush_queue(struct cgpu_info *cgpu) +{ + struct work *work, *tmp; + int discarded = 0; + + wr_lock(&cgpu->qlock); + HASH_ITER(hh, cgpu->queued_work, work, tmp) { + /* Can only discard the work items if they're not physically + * queued on the device. */ + if (!work->queued) { + HASH_DEL(cgpu->queued_work, work); + discard_work(work); + discarded++; + } + } + wr_unlock(&cgpu->qlock); + + if (discarded) + applog(LOG_DEBUG, "Discarded %d queued work items", discarded); +} + +/* This version of hash work is for devices that are fast enough to always + * perform a full nonce range and need a queue to maintain the device busy. + * Work creation and destruction is not done from within this function + * directly. */ +void hash_queued_work(struct thr_info *mythr) +{ + const long cycle = opt_log_interval / 5 ? : 1; + struct timeval tv_start = {0, 0}, tv_end; + struct cgpu_info *cgpu = mythr->cgpu; + struct device_drv *drv = cgpu->drv; + const int thr_id = mythr->id; + int64_t hashes_done = 0; + + while (42) { + struct timeval diff; + int64_t hashes; + + mythr->work_restart = false; + + fill_queue(mythr, cgpu, drv, thr_id); + + thread_reportin(mythr); + hashes = drv->scanwork(mythr); + if (unlikely(hashes == -1 )) { + applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id); + cgpu->deven = DEV_DISABLED; + dev_error(cgpu, REASON_THREAD_ZERO_HASH); + mt_disable(mythr, thr_id, drv); + } + + hashes_done += hashes; + gettimeofday(&tv_end, NULL); + timersub(&tv_end, &tv_start, &diff); + if (diff.tv_sec >= cycle) { + hashmeter(thr_id, &diff, hashes_done); + hashes_done = 0; + memcpy(&tv_start, &tv_end, sizeof(struct timeval)); + } + + if (unlikely(mythr->work_restart)) { + flush_queue(cgpu); + drv->flush_work(cgpu); + } + + if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED)) + mt_disable(mythr, thr_id, drv); + } +} + +void *miner_thread(void *userdata) +{ + struct thr_info *mythr = userdata; + const int thr_id = mythr->id; + struct cgpu_info *cgpu = mythr->cgpu; + struct device_drv *drv = cgpu->drv; + char threadname[20]; + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + snprintf(threadname,20,"miner %d",thr_id); + RenameThread(threadname); + + if (!drv->thread_init(mythr)) { + dev_error(cgpu, REASON_THREAD_FAIL_INIT); + goto out; + } + + thread_reportout(mythr); + applog(LOG_DEBUG, "Popping ping in miner thread"); + tq_pop(mythr->q, NULL); /* Wait for a ping to start */ + + drv->hash_work(mythr); out: - if (api->thread_shutdown) - api->thread_shutdown(mythr); + drv->thread_shutdown(mythr); thread_reportin(mythr); applog(LOG_ERR, "Thread %d failure, exiting", thr_id); @@ -5663,7 +5886,7 @@ retry_pool: if (!pool) { applog(LOG_WARNING, "No suitable long-poll found for %s", cp->rpc_url); while (!pool) { - sleep(60); + nmsleep(60000); pool = select_longpoll_pool(cp); } } @@ -5738,7 +5961,7 @@ retry_pool: continue; if (failures == 1) applog(LOG_WARNING, "longpoll failed for %s, retrying every 30s", lp_url); - sleep(30); + nmsleep(30000); } if (pool != cp) { @@ -5764,8 +5987,7 @@ out: void reinit_device(struct cgpu_info *cgpu) { - if (cgpu->api->reinit_device) - cgpu->api->reinit_device(cgpu); + cgpu->drv->reinit_device(cgpu); } static struct timeval rotate_tv; @@ -5843,7 +6065,7 @@ static void *watchpool_thread(void __maybe_unused *userdata) switch_pools(NULL); } - sleep(30); + nmsleep(30000); } return NULL; @@ -5907,12 +6129,10 @@ static void *watchdog_thread(void __maybe_unused *userdata) applog(LOG_WARNING, "Will restart execution as scheduled at %02d:%02d", schedstart.tm.tm_hour, schedstart.tm.tm_min); sched_paused = true; - for (i = 0; i < mining_threads; i++) { - struct thr_info *thr; - thr = &thr_info[i]; - - thr->pause = true; - } + mutex_lock(&mining_thr_lock); + for (i = 0; i < mining_threads; i++) + mining_thr[i]->pause = true; + mutex_unlock(&mining_thr_lock); } else if (sched_paused && should_run()) { applog(LOG_WARNING, "Restarting execution as per start time %02d:%02d scheduled", schedstart.tm.tm_hour, schedstart.tm.tm_min); @@ -5923,7 +6143,8 @@ static void *watchdog_thread(void __maybe_unused *userdata) for (i = 0; i < mining_threads; i++) { struct thr_info *thr; - thr = &thr_info[i]; + + thr = get_thread(i); /* Don't touch disabled devices */ if (thr->cgpu->deven == DEV_DISABLED) @@ -5934,18 +6155,17 @@ static void *watchdog_thread(void __maybe_unused *userdata) } for (i = 0; i < total_devices; ++i) { - struct cgpu_info *cgpu = devices[i]; + struct cgpu_info *cgpu = get_devices(i); struct thr_info *thr = cgpu->thr[0]; enum dev_enable *denable; char dev_str[8]; int gpu; - if (cgpu->api->get_stats) - cgpu->api->get_stats(cgpu); + cgpu->drv->get_stats(cgpu); gpu = cgpu->device_id; denable = &cgpu->deven; - sprintf(dev_str, "%s%d", cgpu->api->name, gpu); + sprintf(dev_str, "%s%d", cgpu->drv->name, gpu); #ifdef HAVE_ADL if (adl_active && cgpu->has_adl) @@ -5965,7 +6185,7 @@ static void *watchdog_thread(void __maybe_unused *userdata) continue; #ifdef WANT_CPUMINE - if (!strcmp(cgpu->api->dname, "cpu")) + if (cgpu->drv->drv_id == DRIVER_CPU) continue; #endif if (cgpu->status != LIFE_WELL && (now.tv_sec - thr->last.tv_sec < WATCHDOG_SICK_TIME)) { @@ -6022,7 +6242,7 @@ static void log_print_status(struct cgpu_info *cgpu) applog(LOG_WARNING, "%s", logline); } -static void print_summary(void) +void print_summary(void) { struct timeval diff; int hours, mins, secs, i; @@ -6102,8 +6322,11 @@ static void print_summary(void) } applog(LOG_WARNING, "Summary of per device statistics:\n"); - for (i = 0; i < total_devices; ++i) - log_print_status(devices[i]); + for (i = 0; i < total_devices; ++i) { + struct cgpu_info *cgpu = get_devices(i); + + log_print_status(cgpu); + } if (opt_shares) applog(LOG_WARNING, "Mined %d accepted shares of %d requested\n", total_accepted, opt_shares); @@ -6353,46 +6576,147 @@ void enable_curses(void) { } #endif -/* TODO: fix need a dummy CPU device_api even if no support for CPU mining */ +/* TODO: fix need a dummy CPU device_drv even if no support for CPU mining */ #ifndef WANT_CPUMINE -struct device_api cpu_api; -struct device_api cpu_api = { +struct device_drv cpu_drv; +struct device_drv cpu_drv = { + .drv_id = DRIVER_CPU, .name = "CPU", }; #endif #ifdef USE_BITFORCE -extern struct device_api bitforce_api; +extern struct device_drv bitforce_drv; #endif #ifdef USE_ICARUS -extern struct device_api icarus_api; +extern struct device_drv icarus_drv; #endif #ifdef USE_MODMINER -extern struct device_api modminer_api; +extern struct device_drv modminer_drv; #endif #ifdef USE_ZTEX -extern struct device_api ztex_api; +extern struct device_drv ztex_drv; #endif - static int cgminer_id_count = 0; +/* Various noop functions for drivers that don't support or need their + * variants. */ +static void noop_reinit_device(struct cgpu_info __maybe_unused *cgpu) +{ +} + +void blank_get_statline_before(char *buf, struct cgpu_info __maybe_unused *cgpu) +{ + tailsprintf(buf, " | "); +} + +static void noop_get_statline(char __maybe_unused *buf, struct cgpu_info __maybe_unused *cgpu) +{ +} + +static bool noop_get_stats(struct cgpu_info __maybe_unused *cgpu) +{ + return true; +} + +static bool noop_thread_prepare(struct thr_info __maybe_unused *thr) +{ + return true; +} + +static uint64_t noop_can_limit_work(struct thr_info __maybe_unused *thr) +{ + return 0xffffffff; +} + +static bool noop_thread_init(struct thr_info __maybe_unused *thr) +{ + return true; +} + +static bool noop_prepare_work(struct thr_info __maybe_unused *thr, struct work __maybe_unused *work) +{ + return true; +} + +static void noop_hw_error(struct thr_info __maybe_unused *thr) +{ +} + +static void noop_thread_shutdown(struct thr_info __maybe_unused *thr) +{ +} + +static void noop_thread_enable(struct thr_info __maybe_unused *thr) +{ +} + +#define noop_flush_work noop_reinit_device +#define noop_queue_full noop_get_stats + +/* Fill missing driver api functions with noops */ +void fill_device_api(struct cgpu_info *cgpu) +{ + struct device_drv *drv = cgpu->drv; + + if (!drv->reinit_device) + drv->reinit_device = &noop_reinit_device; + if (!drv->get_statline_before) + drv->get_statline_before = &blank_get_statline_before; + if (!drv->get_statline) + drv->get_statline = &noop_get_statline; + if (!drv->get_stats) + drv->get_stats = &noop_get_stats; + if (!drv->thread_prepare) + drv->thread_prepare = &noop_thread_prepare; + if (!drv->can_limit_work) + drv->can_limit_work = &noop_can_limit_work; + if (!drv->thread_init) + drv->thread_init = &noop_thread_init; + if (!drv->prepare_work) + drv->prepare_work = &noop_prepare_work; + if (!drv->hw_error) + drv->hw_error = &noop_hw_error; + if (!drv->thread_shutdown) + drv->thread_shutdown = &noop_thread_shutdown; + if (!drv->thread_enable) + drv->thread_enable = &noop_thread_enable; + if (!drv->hash_work) + drv->hash_work = &hash_sole_work; + if (!drv->flush_work) + drv->flush_work = &noop_flush_work; + if (!drv->queue_full) + drv->queue_full = &noop_queue_full; +} + void enable_device(struct cgpu_info *cgpu) { cgpu->deven = DEV_ENABLED; + mutex_lock(&devices_lock); devices[cgpu->cgminer_id = cgminer_id_count++] = cgpu; - mining_threads += cgpu->threads; + mutex_unlock(&devices_lock); + if (hotplug_mode) { + new_threads += cgpu->threads; + adj_width(mining_threads + new_threads, &dev_width); + } else { + mining_threads += cgpu->threads; #ifdef HAVE_CURSES - adj_width(mining_threads, &dev_width); + adj_width(mining_threads, &dev_width); #endif + } #ifdef HAVE_OPENCL - if (cgpu->api == &opencl_api) { + if (cgpu->drv->drv_id == DRIVER_OPENCL) { gpu_threads += cgpu->threads; } #endif + fill_device_api(cgpu); + + rwlock_init(&cgpu->qlock); + cgpu->queued_work = NULL; } struct _cgpu_devid_counter { @@ -6406,20 +6730,137 @@ 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); + HASH_FIND_STR(devids, cgpu->drv->name, d); if (d) cgpu->device_id = ++d->lastid; else { d = malloc(sizeof(*d)); - memcpy(d->name, cgpu->api->name, sizeof(d->name)); + memcpy(d->name, cgpu->drv->name, sizeof(d->name)); cgpu->device_id = d->lastid = 0; HASH_ADD_STR(devids, name, d); } - devices = realloc(devices, sizeof(struct cgpu_info *) * (total_devices + 2)); - devices[total_devices++] = cgpu; + mutex_lock(&devices_lock); + devices = realloc(devices, sizeof(struct cgpu_info *) * (total_devices + new_devices + 2)); + mutex_unlock(&devices_lock); + if (hotplug_mode) + devices[total_devices + new_devices++] = cgpu; + else + devices[total_devices++] = cgpu; return true; } +struct device_drv *copy_drv(struct device_drv *drv) +{ + struct device_drv *copy; + char buf[100]; + + if (unlikely(!(copy = malloc(sizeof(*copy))))) { + sprintf(buf, "Failed to allocate device_drv copy of %s (%s)", + drv->name, drv->copy ? "copy" : "original"); + quit(1, buf); + } + memcpy(copy, drv, sizeof(*copy)); + copy->copy = true; + return copy; +} + +#if defined(USE_MODMINER) || defined(USE_BITFORCE) +static void hotplug_process() +{ + struct thr_info *thr; + int i, j; + + for (i = 0; i < new_devices; i++) { + struct cgpu_info *cgpu = devices[total_devices + i]; + enable_device(cgpu); + cgpu->cgminer_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET; + cgpu->rolling = cgpu->total_mhashes = 0; + } + + mutex_lock(&mining_thr_lock); + mining_thr = realloc(mining_thr, sizeof(thr) * (mining_threads + new_threads + 1)); + mutex_unlock(&mining_thr_lock); + if (!mining_thr) + quit(1, "Failed to hotplug realloc mining_thr"); + for (i = 0; i < new_threads; i++) { + mining_thr[mining_threads + i] = calloc(1, sizeof(*thr)); + if (!mining_thr[mining_threads + i]) + quit(1, "Failed to hotplug calloc mining_thr[%d]", i); + } + + // Start threads + for (i = 0; i < new_devices; ++i) { + struct cgpu_info *cgpu = devices[total_devices]; + cgpu->thr = malloc(sizeof(*cgpu->thr) * (cgpu->threads+1)); + cgpu->thr[cgpu->threads] = NULL; + cgpu->status = LIFE_INIT; + + for (j = 0; j < cgpu->threads; ++j) { + thr = get_thread(mining_threads); + thr->id = mining_threads; + thr->cgpu = cgpu; + thr->device_thread = j; + + thr->q = tq_new(); + if (!thr->q) + quit(1, "tq_new hotplug failed in starting %s%d mining thread (#%d)", cgpu->drv->name, cgpu->device_id, total_devices); + + /* Enable threads for devices set not to mine but disable + * their queue in case we wish to enable them later */ + if (cgpu->deven != DEV_DISABLED) { + applog(LOG_DEBUG, "Pushing hotplug ping to thread %d", thr->id); + tq_push(thr->q, &ping); + } + + if (cgpu->drv->thread_prepare && !cgpu->drv->thread_prepare(thr)) + continue; + + thread_reportout(thr); + + if (unlikely(thr_info_create(thr, NULL, miner_thread, thr))) + quit(1, "hotplug thread %d create failed", thr->id); + + cgpu->thr[j] = thr; + + mining_threads++; + } + total_devices++; + applog(LOG_WARNING, "Hotplug: %s added %s %i", cgpu->drv->dname, cgpu->drv->name, cgpu->device_id); + } +} + +static void *hotplug_thread(void __maybe_unused *userdata) +{ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + RenameThread("hotplug"); + + hotplug_mode = true; + + while (0x2a) { + nmsleep(5000); + +// Version 0.1 just add the devices on - worry about using nodev later + + new_devices = 0; + new_threads = 0; + +#ifdef USE_BITFORCE + bitforce_drv.drv_detect(); +#endif + +#ifdef USE_MODMINER + modminer_drv.drv_detect(); +#endif + + if (new_devices) + hotplug_process(); + } + + return NULL; +} +#endif + int main(int argc, char *argv[]) { bool pools_active = false; @@ -6460,6 +6901,8 @@ int main(int argc, char *argv[]) mutex_init(&sshare_lock); rwlock_init(&blk_lock); rwlock_init(&netacc_lock); + mutex_init(&mining_thr_lock); + mutex_init(&devices_lock); mutex_init(&lp_lock); if (unlikely(pthread_cond_init(&lp_cond, NULL))) @@ -6629,32 +7072,32 @@ int main(int argc, char *argv[]) #ifdef HAVE_OPENCL if (!opt_nogpu) - opencl_api.api_detect(); + opencl_drv.drv_detect(); gpu_threads = 0; #endif #ifdef USE_ICARUS if (!opt_scrypt) - icarus_api.api_detect(); + icarus_drv.drv_detect(); #endif #ifdef USE_BITFORCE if (!opt_scrypt) - bitforce_api.api_detect(); + bitforce_drv.drv_detect(); #endif #ifdef USE_MODMINER if (!opt_scrypt) - modminer_api.api_detect(); + modminer_drv.drv_detect(); #endif #ifdef USE_ZTEX if (!opt_scrypt) - ztex_api.api_detect(); + ztex_drv.drv_detect(); #endif #ifdef WANT_CPUMINE - cpu_api.api_detect(); + cpu_drv.drv_detect(); #endif if (devices_enabled == -1) { @@ -6662,9 +7105,9 @@ int main(int argc, char *argv[]) for (i = 0; i < total_devices; ++i) { 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); + applog(LOG_ERR, " %2d. %s %d: %s (driver: %s)", i, cgpu->drv->name, cgpu->device_id, cgpu->name, cgpu->drv->dname); else - applog(LOG_ERR, " %2d. %s %d (driver: %s)", i, cgpu->api->name, cgpu->device_id, cgpu->api->dname); + applog(LOG_ERR, " %2d. %s %d (driver: %s)", i, cgpu->drv->name, cgpu->device_id, cgpu->drv->dname); } quit(0, "%d devices listed", total_devices); } @@ -6678,7 +7121,7 @@ int main(int argc, char *argv[]) enable_device(devices[i]); } else if (i < total_devices) { if (opt_removedisabled) { - if (devices[i]->api == &cpu_api) + if (devices[i]->drv->drv_id == DRIVER_CPU) --opt_n_threads; } else { enable_device(devices[i]); @@ -6695,6 +7138,8 @@ int main(int argc, char *argv[]) if (!total_devices) quit(1, "All devices disabled, cannot mine!"); + start_devices = total_devices; + load_temp_cutoffs(); for (i = 0; i < total_devices; ++i) @@ -6744,14 +7189,23 @@ int main(int argc, char *argv[]) fork_monitor(); #endif // defined(unix) - total_threads = mining_threads + 7; - thr_info = calloc(total_threads, sizeof(*thr)); - if (!thr_info) - quit(1, "Failed to calloc thr_info"); + mining_thr = calloc(mining_threads, sizeof(thr)); + if (!mining_thr) + quit(1, "Failed to calloc mining_thr"); + for (i = 0; i < mining_threads; i++) { + mining_thr[i] = calloc(1, sizeof(*thr)); + if (!mining_thr[i]) + quit(1, "Failed to calloc mining_thr[%d]", i); + } - gwsched_thr_id = mining_threads; - stage_thr_id = mining_threads + 1; - thr = &thr_info[stage_thr_id]; + total_control_threads = 8; + control_thr = calloc(total_control_threads, sizeof(*thr)); + if (!control_thr) + quit(1, "Failed to calloc control_thr"); + + gwsched_thr_id = 0; + stage_thr_id = 1; + thr = &control_thr[stage_thr_id]; thr->q = tq_new(); if (!thr->q) quit(1, "Failed to tq_new"); @@ -6843,14 +7297,14 @@ begin_bench: cgpu->status = LIFE_INIT; for (j = 0; j < cgpu->threads; ++j, ++k) { - thr = &thr_info[k]; + thr = get_thread(k); thr->id = k; thr->cgpu = cgpu; thr->device_thread = j; thr->q = tq_new(); if (!thr->q) - quit(1, "tq_new failed in starting %s%d mining thread (#%d)", cgpu->api->name, cgpu->device_id, i); + quit(1, "tq_new failed in starting %s%d mining thread (#%d)", cgpu->drv->name, cgpu->device_id, i); /* Enable threads for devices set not to mine but disable * their queue in case we wish to enable them later */ @@ -6860,7 +7314,7 @@ begin_bench: tq_push(thr->q, &ping); } - if (cgpu->api->thread_prepare && !cgpu->api->thread_prepare(thr)) + if (!cgpu->drv->thread_prepare(thr)) continue; thread_reportout(thr); @@ -6888,15 +7342,15 @@ begin_bench: gettimeofday(&total_tv_start, NULL); gettimeofday(&total_tv_end, NULL); - watchpool_thr_id = mining_threads + 2; - thr = &thr_info[watchpool_thr_id]; + watchpool_thr_id = 2; + thr = &control_thr[watchpool_thr_id]; /* start watchpool thread */ if (thr_info_create(thr, NULL, watchpool_thread, NULL)) quit(1, "watchpool thread create failed"); pthread_detach(thr->pth); - watchdog_thr_id = mining_threads + 3; - thr = &thr_info[watchdog_thr_id]; + watchdog_thr_id = 3; + thr = &control_thr[watchdog_thr_id]; /* start watchdog thread */ if (thr_info_create(thr, NULL, watchdog_thread, NULL)) quit(1, "watchdog thread create failed"); @@ -6904,8 +7358,8 @@ begin_bench: #ifdef HAVE_OPENCL /* Create reinit gpu thread */ - gpur_thr_id = mining_threads + 4; - thr = &thr_info[gpur_thr_id]; + gpur_thr_id = 4; + thr = &control_thr[gpur_thr_id]; thr->q = tq_new(); if (!thr->q) quit(1, "tq_new failed for gpur_thr_id"); @@ -6914,22 +7368,36 @@ begin_bench: #endif /* Create API socket thread */ - api_thr_id = mining_threads + 5; - thr = &thr_info[api_thr_id]; + api_thr_id = 5; + thr = &control_thr[api_thr_id]; if (thr_info_create(thr, NULL, api_thread, thr)) quit(1, "API thread create failed"); +#if defined(USE_MODMINER) || defined(USE_BITFORCE) + if (!opt_scrypt) { + hotplug_thr_id = 6; + thr = &control_thr[hotplug_thr_id]; + if (thr_info_create(thr, NULL, hotplug_thread, thr)) + quit(1, "hotplug thread create failed"); + pthread_detach(thr->pth); + } +#endif + #ifdef HAVE_CURSES /* Create curses input thread for keyboard input. Create this last so * that we know all threads are created since this can call kill_work - * to try and shut down ll previous threads. */ - input_thr_id = mining_threads + 6; - thr = &thr_info[input_thr_id]; + * to try and shut down all previous threads. */ + input_thr_id = 7; + thr = &control_thr[input_thr_id]; if (thr_info_create(thr, NULL, input_thread, thr)) quit(1, "input thread create failed"); pthread_detach(thr->pth); #endif + /* Just to be sure */ + if (total_control_threads != 8) + quit(1, "incorrect total_control_threads (%d) should be 8", total_control_threads); + /* Once everything is set up, main() becomes the getwork scheduler */ while (42) { int ts, max_staged = opt_queue; @@ -6974,7 +7442,7 @@ retry: while (!pool->stratum_active || !pool->stratum_notify) { struct pool *altpool = select_pool(true); - sleep(5); + nmsleep(5000); if (altpool != pool) { pool = altpool; goto retry; @@ -6990,7 +7458,7 @@ retry: while (pool->idle) { struct pool *altpool = select_pool(true); - sleep(5); + nmsleep(5000); if (altpool != pool) { pool = altpool; goto retry; @@ -7024,7 +7492,7 @@ retry: * requests but is up as we'll keep hammering it */ if (++pool->seq_getfails > mining_threads + opt_queue) pool_died(pool); - sleep(5); + nmsleep(5000); push_curl_entry(ce, pool); pool = select_pool(!opt_fail_only); goto retry; diff --git a/configure.ac b/configure.ac index 1fe2acc9..1415bba8 100644 --- a/configure.ac +++ b/configure.ac @@ -273,6 +273,7 @@ fi AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$ztex != xnononono]) +AM_CONDITIONAL([NEED_USBUTILS_C], [test x$bitforce$modminer != xnono]) AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes]) AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue]) AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue]) @@ -321,7 +322,7 @@ fi AM_CONDITIONAL([HAS_YASM], [test x$has_yasm = xtrue]) -if test "x$bitforce" != xno; then +if test "x$icarus" != xno; then AC_ARG_WITH([libudev], [AC_HELP_STRING([--without-libudev], [Autodetect FPGAs using libudev (default enabled)])], [libudev=$withval], [libudev=auto] @@ -343,7 +344,7 @@ AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno]) PKG_PROG_PKG_CONFIG() -if test "x$ztex$modminer" != xnono; then +if test "x$ztex$modminer$bitforce" != xnonono; then case $target in *-*-freebsd*) LIBUSB_LIBS="-lusb" @@ -508,7 +509,7 @@ else echo " Ztex.FPGAs...........: Disabled" fi -if test "x$bitforce" != xno; then +if test "x$icarus" != xno; then echo " libudev.detection....: $libudev" fi diff --git a/driver-bitforce.c b/driver-bitforce.c index b8a83430..228fa5e7 100644 --- a/driver-bitforce.c +++ b/driver-bitforce.c @@ -1,4 +1,5 @@ /* + * Copyright 2012-2013 Andrew Smith * Copyright 2012 Luke Dashjr * Copyright 2012 Con Kolivas * @@ -19,34 +20,35 @@ #include "config.h" #ifdef WIN32 - #include - -#define dlsym (void*)GetProcAddress -#define dlclose FreeLibrary - -typedef unsigned long FT_STATUS; -typedef PVOID FT_HANDLE; -__stdcall FT_STATUS (*FT_ListDevices)(PVOID pArg1, PVOID pArg2, DWORD Flags); -__stdcall FT_STATUS (*FT_Open)(int idx, FT_HANDLE*); -__stdcall FT_STATUS (*FT_GetComPortNumber)(FT_HANDLE, LPLONG lplComPortNumber); -__stdcall FT_STATUS (*FT_Close)(FT_HANDLE); -const uint32_t FT_OPEN_BY_DESCRIPTION = 2; -const uint32_t FT_LIST_ALL = 0x20000000; -const uint32_t FT_LIST_NUMBER_ONLY = 0x80000000; -enum { - FT_OK, -}; - -// Code must deal with a timeout. Make it 1 second on windows, 0.1 on linux. -#define BFopen(devpath) serial_open(devpath, 0, 10, true) -#else /* WIN32 */ -#define BFopen(devpath) serial_open(devpath, 0, 1, true) #endif /* WIN32 */ #include "compat.h" #include "miner.h" -#include "fpgautils.h" +#include "usbutils.h" + +#define BITFORCE_IDENTIFY "ZGX" +#define BITFORCE_IDENTIFY_LEN (sizeof(BITFORCE_IDENTIFY)-1) +#define BITFORCE_FLASH "ZMX" +#define BITFORCE_FLASH_LEN (sizeof(BITFORCE_FLASH)-1) +#define BITFORCE_TEMPERATURE "ZLX" +#define BITFORCE_TEMPERATURE_LEN (sizeof(BITFORCE_TEMPERATURE)-1) +#define BITFORCE_SENDRANGE "ZPX" +#define BITFORCE_SENDRANGE_LEN (sizeof(BITFORCE_SENDRANGE)-1) +#define BITFORCE_SENDWORK "ZDX" +#define BITFORCE_SENDWORK_LEN (sizeof(BITFORCE_SENDWORK)-1) +#define BITFORCE_WORKSTATUS "ZFX" +#define BITFORCE_WORKSTATUS_LEN (sizeof(BITFORCE_WORKSTATUS)-1) + +// Either of Nonce or No-nonce start with: +#define BITFORCE_EITHER "N" +#define BITFORCE_EITHER_LEN 1 +#define BITFORCE_NONCE "NONCE-FOUND" +#define BITFORCE_NONCE_LEN (sizeof(BITFORCE_NONCE)-1) +#define BITFORCE_NO_NONCE "NO-NONCE" +#define BITFORCE_NO_NONCE_MATCH 3 +#define BITFORCE_IDLE "IDLE" +#define BITFORCE_IDLE_MATCH 1 #define BITFORCE_SLEEP_MS 500 #define BITFORCE_TIMEOUT_S 7 @@ -62,62 +64,200 @@ enum { #define KNAME_WORK "full work" #define KNAME_RANGE "nonce range" -struct device_api bitforce_api; +#define BITFORCE_BUFSIZ (0x200) -static void BFgets(char *buf, size_t bufLen, int fd) -{ - do { - buf[0] = '\0'; - --bufLen; - } while (likely(bufLen && read(fd, buf, 1) == 1 && (buf++)[0] != '\n')); +// If initialisation fails the first time, +// sleep this amount (ms) and try again +#define REINIT_TIME_FIRST_MS 100 +// Max ms per sleep +#define REINIT_TIME_MAX_MS 800 +// Keep trying up to this many us +#define REINIT_TIME_MAX 3000000 - buf[0] = '\0'; -} +static const char *blank = ""; + +struct device_drv bitforce_drv; -static ssize_t BFwrite(int fd, const void *buf, ssize_t bufLen) +static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) { - if ((bufLen) != write(fd, buf, bufLen)) - return 0; - else - return bufLen; -} + int err; -#define BFclose(fd) close(fd) + if (lock) + mutex_lock(&bitforce->device_mutex); -static bool bitforce_detect_one(const char *devpath) + if (bitforce->usbinfo.nodev) + goto failed; + + // Reset + err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, + FTDI_VALUE_RESET, bitforce->usbdev->found->interface, C_RESET); + if (opt_debug) + applog(LOG_DEBUG, "%s%i: reset got err %d", + bitforce->drv->name, bitforce->device_id, err); + + if (bitforce->usbinfo.nodev) + goto failed; + + // Set data control + err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, + FTDI_VALUE_DATA, bitforce->usbdev->found->interface, C_SETDATA); + if (opt_debug) + applog(LOG_DEBUG, "%s%i: setdata got err %d", + bitforce->drv->name, bitforce->device_id, err); + + if (bitforce->usbinfo.nodev) + goto failed; + + // Set the baud + err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD, + (FTDI_INDEX_BAUD & 0xff00) | bitforce->usbdev->found->interface, + C_SETBAUD); + if (opt_debug) + applog(LOG_DEBUG, "%s%i: setbaud got err %d", + bitforce->drv->name, bitforce->device_id, err); + + if (bitforce->usbinfo.nodev) + goto failed; + + // Set Flow Control + err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, + FTDI_VALUE_FLOW, bitforce->usbdev->found->interface, C_SETFLOW); + if (opt_debug) + applog(LOG_DEBUG, "%s%i: setflowctrl got err %d", + bitforce->drv->name, bitforce->device_id, err); + + if (bitforce->usbinfo.nodev) + goto failed; + + // Set Modem Control + err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, + FTDI_VALUE_MODEM, bitforce->usbdev->found->interface, C_SETMODEM); + if (opt_debug) + applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d", + bitforce->drv->name, bitforce->device_id, err); + + if (bitforce->usbinfo.nodev) + goto failed; + + // Clear any sent data + err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, + FTDI_VALUE_PURGE_TX, bitforce->usbdev->found->interface, C_PURGETX); + if (opt_debug) + applog(LOG_DEBUG, "%s%i: purgetx got err %d", + bitforce->drv->name, bitforce->device_id, err); + + if (bitforce->usbinfo.nodev) + goto failed; + + // Clear any received data + err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, + FTDI_VALUE_PURGE_RX, bitforce->usbdev->found->interface, C_PURGERX); + if (opt_debug) + applog(LOG_DEBUG, "%s%i: purgerx got err %d", + bitforce->drv->name, bitforce->device_id, err); + +failed: + + if (lock) + mutex_unlock(&bitforce->device_mutex); +} + +static bool bitforce_detect_one(struct libusb_device *dev, struct usb_find_devices *found) { - int fdDev = BFopen(devpath); - struct cgpu_info *bitforce; - char pdevbuf[0x100]; + char buf[BITFORCE_BUFSIZ+1]; + char devpath[20]; + int err, amount; char *s; + struct timeval init_start, init_now; + int init_sleep, init_count; + bool ident_first; - applog(LOG_DEBUG, "BFL: Attempting to open %s", devpath); + struct cgpu_info *bitforce = NULL; + bitforce = calloc(1, sizeof(*bitforce)); + bitforce->drv = &bitforce_drv; + bitforce->deven = DEV_ENABLED; + bitforce->threads = 1; - if (unlikely(fdDev == -1)) { - applog(LOG_ERR, "BFL: Failed to open %s", devpath); - return false; + if (!usb_init(bitforce, dev, found)) { + applog(LOG_ERR, "%s detect (%d:%d) failed to initialise (incorrect device?)", + bitforce->drv->dname, + (int)(bitforce->usbinfo.bus_number), + (int)(bitforce->usbinfo.device_address)); + goto shin; } - BFwrite(fdDev, "ZGX", 3); - pdevbuf[0] = '\0'; - BFgets(pdevbuf, sizeof(pdevbuf), fdDev); - if (unlikely(!pdevbuf[0])) { - applog(LOG_ERR, "BFL: Error reading/timeout (ZGX)"); - return 0; + sprintf(devpath, "%d:%d", + (int)(bitforce->usbinfo.bus_number), + (int)(bitforce->usbinfo.device_address)); + + + // Allow 2 complete attempts if the 1st time returns an unrecognised reply + ident_first = true; +retry: + init_count = 0; + init_sleep = REINIT_TIME_FIRST_MS; + gettimeofday(&init_start, NULL); +reinit: + bitforce_initialise(bitforce, false); + if ((err = usb_write(bitforce, BITFORCE_IDENTIFY, BITFORCE_IDENTIFY_LEN, &amount, C_REQUESTIDENTIFY)) < 0 || amount != BITFORCE_IDENTIFY_LEN) { + applog(LOG_ERR, "%s detect (%s) send identify request failed (%d:%d)", + bitforce->drv->dname, devpath, amount, err); + goto unshin; } - BFclose(fdDev); - if (unlikely(!strstr(pdevbuf, "SHA256"))) { - applog(LOG_ERR, "BFL: Didn't recognise BitForce on %s", devpath); - return false; + if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETIDENTIFY)) < 0 || amount < 1) { + init_count++; + gettimeofday(&init_now, NULL); + if (us_tdiff(&init_now, &init_start) <= REINIT_TIME_MAX) { + if (init_count == 2) { + applog(LOG_WARNING, "%s detect (%s) 2nd init failed (%d:%d) - retrying", + bitforce->drv->dname, devpath, amount, err); + } + nmsleep(init_sleep); + if ((init_sleep * 2) <= REINIT_TIME_MAX_MS) + init_sleep *= 2; + goto reinit; + } + + if (init_count > 0) + applog(LOG_WARNING, "%s detect (%s) init failed %d times %.2fs", + bitforce->drv->dname, devpath, init_count, tdiff(&init_now, &init_start)); + + if (err < 0) { + applog(LOG_ERR, "%s detect (%s) error identify reply (%d:%d)", + bitforce->drv->dname, devpath, amount, err); + } else { + applog(LOG_ERR, "%s detect (%s) empty identify reply (%d)", + bitforce->drv->dname, devpath, amount); + } + + goto unshin; + } + buf[amount] = '\0'; + + if (unlikely(!strstr(buf, "SHA256"))) { + if (ident_first) { + applog(LOG_WARNING, "%s detect (%s) didn't recognise '%s' trying again ...", + bitforce->drv->dname, devpath, buf); + ident_first = false; + goto retry; + } + applog(LOG_ERR, "%s detect (%s) didn't recognise '%s' on 2nd attempt", + bitforce->drv->dname, devpath, buf); + goto unshin; + } + + if (likely((!memcmp(buf, ">>>ID: ", 7)) && (s = strstr(buf + 3, ">>>")))) { + s[0] = '\0'; + bitforce->name = strdup(buf + 7); + } else { + bitforce->name = (char *)blank; } // We have a real BitForce! - bitforce = calloc(1, sizeof(*bitforce)); - bitforce->api = &bitforce_api; - bitforce->device_path = strdup(devpath); - bitforce->deven = DEV_ENABLED; - bitforce->threads = 1; + applog(LOG_DEBUG, "%s (%s) identified as: '%s'", + bitforce->drv->dname, devpath, bitforce->name); + /* Initially enable support for nonce range and disable it later if it * fails */ if (opt_bfl_noncerange) { @@ -129,108 +269,39 @@ static bool bitforce_detect_one(const char *devpath) bitforce->kname = KNAME_WORK; } - if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>")))) { - s[0] = '\0'; - bitforce->name = strdup(pdevbuf + 7); - } + bitforce->device_path = strdup(devpath); + + if (!add_cgpu(bitforce)) + goto unshin; + + update_usb_stats(bitforce); mutex_init(&bitforce->device_mutex); - return add_cgpu(bitforce); -} + return true; -#define LOAD_SYM(sym) do { \ - if (!(sym = dlsym(dll, #sym))) { \ - applog(LOG_DEBUG, "Failed to load " #sym ", not using FTDI bitforce autodetect"); \ - goto out; \ - } \ -} while(0) +unshin: -#ifdef WIN32 -static int bitforce_autodetect_ftdi(void) -{ - char devpath[] = "\\\\.\\COMnnnnn"; - char *devpathnum = &devpath[7]; - char **bufptrs; - char *buf; - int found = 0; - DWORD i; - - FT_STATUS ftStatus; - DWORD numDevs; - HMODULE dll = LoadLibrary("FTD2XX.DLL"); - if (!dll) { - applog(LOG_DEBUG, "FTD2XX.DLL failed to load, not using FTDI bitforce autodetect"); - return 0; - } - LOAD_SYM(FT_ListDevices); - LOAD_SYM(FT_Open); - LOAD_SYM(FT_GetComPortNumber); - LOAD_SYM(FT_Close); - - ftStatus = FT_ListDevices(&numDevs, NULL, FT_LIST_NUMBER_ONLY); - if (ftStatus != FT_OK) { - applog(LOG_DEBUG, "FTDI device count failed, not using FTDI bitforce autodetect"); - goto out; - } - applog(LOG_DEBUG, "FTDI reports %u devices", (unsigned)numDevs); - - buf = alloca(65 * numDevs); - bufptrs = alloca(sizeof(*bufptrs) * (numDevs + 1)); - - for (i = 0; i < numDevs; ++i) - bufptrs[i] = &buf[i * 65]; - bufptrs[numDevs] = NULL; - ftStatus = FT_ListDevices(bufptrs, &numDevs, FT_LIST_ALL | FT_OPEN_BY_DESCRIPTION); - if (ftStatus != FT_OK) { - applog(LOG_DEBUG, "FTDI device list failed, not using FTDI bitforce autodetect"); - goto out; - } - - for (i = numDevs; i > 0; ) { - --i; - bufptrs[i][64] = '\0'; - - if (!(strstr(bufptrs[i], "BitFORCE") && strstr(bufptrs[i], "SHA256"))) - continue; - - FT_HANDLE ftHandle; - if (FT_OK != FT_Open(i, &ftHandle)) - continue; - LONG lComPortNumber; - ftStatus = FT_GetComPortNumber(ftHandle, &lComPortNumber); - FT_Close(ftHandle); - if (FT_OK != ftStatus || lComPortNumber < 0) - continue; - - sprintf(devpathnum, "%d", (int)lComPortNumber); - - if (bitforce_detect_one(devpath)) - ++found; - } + usb_uninit(bitforce); -out: - dlclose(dll); - return found; -} -#else -static int bitforce_autodetect_ftdi(void) -{ - return 0; -} -#endif +shin: -static int bitforce_detect_auto(void) -{ - return (serial_autodetect_udev (bitforce_detect_one, "BitFORCE*SHA256") ?: - serial_autodetect_devserial(bitforce_detect_one, "BitFORCE_SHA256") ?: - bitforce_autodetect_ftdi() ?: - 0); + free(bitforce->device_path); + + if (bitforce->name != blank) + free(bitforce->name); + + if (bitforce->drv->copy) + free(bitforce->drv); + + free(bitforce); + + return false; } static void bitforce_detect(void) { - serial_detect_auto(&bitforce_api, bitforce_detect_one, bitforce_detect_auto); + usb_detect(&bitforce_drv, bitforce_detect_one); } static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce) @@ -247,105 +318,17 @@ static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce) static bool bitforce_thread_prepare(struct thr_info *thr) { struct cgpu_info *bitforce = thr->cgpu; - int fdDev = BFopen(bitforce->device_path); struct timeval now; - if (unlikely(fdDev == -1)) { - applog(LOG_ERR, "BFL%i: Failed to open %s", bitforce->device_id, bitforce->device_path); - return false; - } - - bitforce->device_fd = fdDev; - - applog(LOG_INFO, "BFL%i: Opened %s", bitforce->device_id, bitforce->device_path); gettimeofday(&now, NULL); get_datestamp(bitforce->init, &now); return true; } -static void bitforce_clear_buffer(struct cgpu_info *bitforce) -{ - int fdDev = bitforce->device_fd; - char pdevbuf[0x100]; - int count = 0; - - if (!fdDev) - return; - - applog(LOG_DEBUG, "BFL%i: Clearing read buffer", bitforce->device_id); - - mutex_lock(&bitforce->device_mutex); - do { - pdevbuf[0] = '\0'; - BFgets(pdevbuf, sizeof(pdevbuf), fdDev); - } while (pdevbuf[0] && (++count < 10)); - mutex_unlock(&bitforce->device_mutex); -} - -void bitforce_init(struct cgpu_info *bitforce) -{ - char *devpath = bitforce->device_path; - int fdDev = bitforce->device_fd, retries = 0; - char pdevbuf[0x100]; - char *s; - - applog(LOG_WARNING, "BFL%i: Re-initialising", bitforce->device_id); - - bitforce_clear_buffer(bitforce); - - mutex_lock(&bitforce->device_mutex); - if (fdDev) { - BFclose(fdDev); - sleep(5); - } - bitforce->device_fd = 0; - - fdDev = BFopen(devpath); - if (unlikely(fdDev == -1)) { - mutex_unlock(&bitforce->device_mutex); - applog(LOG_ERR, "BFL%i: Failed to open %s", bitforce->device_id, devpath); - return; - } - - do { - BFwrite(fdDev, "ZGX", 3); - pdevbuf[0] = '\0'; - BFgets(pdevbuf, sizeof(pdevbuf), fdDev); - - if (unlikely(!pdevbuf[0])) { - mutex_unlock(&bitforce->device_mutex); - applog(LOG_ERR, "BFL%i: Error reading/timeout (ZGX)", bitforce->device_id); - return; - } - - if (retries++) - nmsleep(10); - } while (!strstr(pdevbuf, "BUSY") && (retries * 10 < BITFORCE_TIMEOUT_MS)); - - if (unlikely(!strstr(pdevbuf, "SHA256"))) { - mutex_unlock(&bitforce->device_mutex); - applog(LOG_ERR, "BFL%i: Didn't recognise BitForce on %s returned: %s", bitforce->device_id, devpath, pdevbuf); - return; - } - - if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>")))) { - s[0] = '\0'; - bitforce->name = strdup(pdevbuf + 7); - } - - bitforce->device_fd = fdDev; - bitforce->sleep_ms = BITFORCE_SLEEP_MS; - - mutex_unlock(&bitforce->device_mutex); -} - static void bitforce_flash_led(struct cgpu_info *bitforce) { - int fdDev = bitforce->device_fd; - - if (!fdDev) - return; + int err, amount; /* Do not try to flash the led if we're polling for a result to * minimise the chance of interleaved results */ @@ -353,19 +336,22 @@ static void bitforce_flash_led(struct cgpu_info *bitforce) return; /* It is not critical flashing the led so don't get stuck if we - * can't grab the mutex here */ + * can't grab the mutex now */ if (mutex_trylock(&bitforce->device_mutex)) return; - BFwrite(fdDev, "ZMX", 3); + if ((err = usb_write(bitforce, BITFORCE_FLASH, BITFORCE_FLASH_LEN, &amount, C_REQUESTFLASH)) < 0 || amount != BITFORCE_FLASH_LEN) { + applog(LOG_ERR, "%s%i: flash request failed (%d:%d)", + bitforce->drv->name, bitforce->device_id, amount, err); + } else { + /* However, this stops anything else getting a reply + * So best to delay any other access to the BFL */ + nmsleep(4000); + } /* Once we've tried - don't do it until told to again */ bitforce->flash_led = false; - /* However, this stops anything else getting a reply - * So best to delay any other access to the BFL */ - sleep(4); - mutex_unlock(&bitforce->device_mutex); return; // nothing is returned by the BFL @@ -373,11 +359,12 @@ static void bitforce_flash_led(struct cgpu_info *bitforce) static bool bitforce_get_temp(struct cgpu_info *bitforce) { - int fdDev = bitforce->device_fd; - char pdevbuf[0x100]; + char buf[BITFORCE_BUFSIZ+1]; + int err, amount; char *s; - if (!fdDev) + // Device is gone + if (bitforce->usbinfo.nodev) return false; /* Do not try to get the temperature if we're polling for a result to @@ -396,18 +383,30 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce) if (mutex_trylock(&bitforce->device_mutex)) return false; - BFwrite(fdDev, "ZLX", 3); - pdevbuf[0] = '\0'; - BFgets(pdevbuf, sizeof(pdevbuf), fdDev); - mutex_unlock(&bitforce->device_mutex); - - if (unlikely(!pdevbuf[0])) { - applog(LOG_ERR, "BFL%i: Error: Get temp returned empty string/timed out", bitforce->device_id); + if ((err = usb_write(bitforce, BITFORCE_TEMPERATURE, BITFORCE_TEMPERATURE_LEN, &amount, C_REQUESTTEMPERATURE)) < 0 || amount != BITFORCE_TEMPERATURE_LEN) { + mutex_unlock(&bitforce->device_mutex); + applog(LOG_ERR, "%s%i: Error: Request temp invalid/timed out (%d:%d)", + bitforce->drv->name, bitforce->device_id, amount, err); bitforce->hw_errors++; return false; } - if ((!strncasecmp(pdevbuf, "TEMP", 4)) && (s = strchr(pdevbuf + 4, ':'))) { + if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETTEMPERATURE)) < 0 || amount < 1) { + mutex_unlock(&bitforce->device_mutex); + if (err < 0) { + applog(LOG_ERR, "%s%i: Error: Get temp return invalid/timed out (%d:%d)", + bitforce->drv->name, bitforce->device_id, amount, err); + } else { + applog(LOG_ERR, "%s%i: Error: Get temp returned nothing (%d:%d)", + bitforce->drv->name, bitforce->device_id, amount, err); + } + bitforce->hw_errors++; + return false; + } + + mutex_unlock(&bitforce->device_mutex); + + if ((!strncasecmp(buf, "TEMP", 4)) && (s = strchr(buf + 4, ':'))) { float temp = strtof(s + 1, NULL); /* Cope with older software that breaks and reads nonsense @@ -418,7 +417,8 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce) if (temp > 0) { bitforce->temp = temp; if (unlikely(bitforce->cutofftemp > 0 && temp > bitforce->cutofftemp)) { - applog(LOG_WARNING, "BFL%i: Hit thermal cutoff limit, disabling!", bitforce->device_id); + applog(LOG_WARNING, "%s%i: Hit thermal cutoff limit, disabling!", + bitforce->drv->name, bitforce->device_id); bitforce->deven = DEV_RECOVER; dev_error(bitforce, REASON_DEV_THERMAL_CUTOFF); } @@ -427,11 +427,12 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce) /* Use the temperature monitor as a kind of watchdog for when * our responses are out of sync and flush the buffer to * hopefully recover */ - applog(LOG_WARNING, "BFL%i: Garbled response probably throttling, clearing buffer", bitforce->device_id); + applog(LOG_WARNING, "%s%i: Garbled response probably throttling, clearing buffer", + bitforce->drv->name, bitforce->device_id); dev_error(bitforce, REASON_DEV_THROTTLE); /* Count throttling episodes as hardware errors */ bitforce->hw_errors++; - bitforce_clear_buffer(bitforce); + bitforce_initialise(bitforce, true); return false; } @@ -441,35 +442,53 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce) static bool bitforce_send_work(struct thr_info *thr, struct work *work) { struct cgpu_info *bitforce = thr->cgpu; - int fdDev = bitforce->device_fd; unsigned char ob[70]; - char pdevbuf[0x100]; + char buf[BITFORCE_BUFSIZ+1]; + int err, amount; char *s; + char *cmd; + int len; - if (!fdDev) - return false; re_send: + if (bitforce->nonce_range) { + cmd = BITFORCE_SENDRANGE; + len = BITFORCE_SENDRANGE_LEN; + } else { + cmd = BITFORCE_SENDWORK; + len = BITFORCE_SENDWORK_LEN; + } + mutex_lock(&bitforce->device_mutex); - if (bitforce->nonce_range) - BFwrite(fdDev, "ZPX", 3); - else - BFwrite(fdDev, "ZDX", 3); - pdevbuf[0] = '\0'; - BFgets(pdevbuf, sizeof(pdevbuf), fdDev); - if (!pdevbuf[0] || !strncasecmp(pdevbuf, "B", 1)) { + if ((err = usb_write(bitforce, cmd, len, &amount, C_REQUESTSENDWORK)) < 0 || amount != len) { + mutex_unlock(&bitforce->device_mutex); + applog(LOG_ERR, "%s%i: request send work failed (%d:%d)", + bitforce->drv->name, bitforce->device_id, amount, err); + return false; + } + + if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_REQUESTSENDWORKSTATUS)) < 0) { + mutex_unlock(&bitforce->device_mutex); + applog(LOG_ERR, "%s%d: read request send work status failed (%d:%d)", + bitforce->drv->name, bitforce->device_id, amount, err); + return false; + } + + if (amount == 0 || !buf[0] || !strncasecmp(buf, "B", 1)) { mutex_unlock(&bitforce->device_mutex); nmsleep(WORK_CHECK_INTERVAL_MS); goto re_send; - } else if (unlikely(strncasecmp(pdevbuf, "OK", 2))) { + } else if (unlikely(strncasecmp(buf, "OK", 2))) { mutex_unlock(&bitforce->device_mutex); if (bitforce->nonce_range) { - applog(LOG_WARNING, "BFL%i: Does not support nonce range, disabling", bitforce->device_id); + applog(LOG_WARNING, "%s%i: Does not support nonce range, disabling", + bitforce->drv->name, bitforce->device_id); bitforce->nonce_range = false; bitforce->sleep_ms *= 5; bitforce->kname = KNAME_WORK; goto re_send; } - applog(LOG_ERR, "BFL%i: Error: Send work reports: %s", bitforce->device_id, pdevbuf); + applog(LOG_ERR, "%s%i: Error: Send work reports: %s", + bitforce->drv->name, bitforce->device_id, buf); return false; } @@ -479,7 +498,7 @@ re_send: if (!bitforce->nonce_range) { sprintf((char *)ob + 8 + 32 + 12, ">>>>>>>>"); work->blk.nonce = bitforce->nonces = 0xffffffff; - BFwrite(fdDev, ob, 60); + len = 60; } else { uint32_t *nonce; @@ -491,26 +510,41 @@ re_send: *nonce = htobe32(work->blk.nonce + bitforce->nonces); work->blk.nonce += bitforce->nonces + 1; sprintf((char *)ob + 8 + 32 + 12 + 8, ">>>>>>>>"); - BFwrite(fdDev, ob, 68); + len = 68; + } + + if ((err = usb_write(bitforce, (char *)ob, len, &amount, C_SENDWORK)) < 0 || amount != len) { + mutex_unlock(&bitforce->device_mutex); + applog(LOG_ERR, "%s%i: send work failed (%d:%d)", + bitforce->drv->name, bitforce->device_id, amount, err); + return false; + } + + if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_SENDWORKSTATUS)) < 0) { + mutex_unlock(&bitforce->device_mutex); + applog(LOG_ERR, "%s%d: read send work status failed (%d:%d)", + bitforce->drv->name, bitforce->device_id, amount, err); + return false; } - pdevbuf[0] = '\0'; - BFgets(pdevbuf, sizeof(pdevbuf), fdDev); mutex_unlock(&bitforce->device_mutex); if (opt_debug) { s = bin2hex(ob + 8, 44); - applog(LOG_DEBUG, "BFL%i: block data: %s", bitforce->device_id, s); + applog(LOG_DEBUG, "%s%i: block data: %s", + bitforce->drv->name, bitforce->device_id, s); free(s); } - if (unlikely(!pdevbuf[0])) { - applog(LOG_ERR, "BFL%i: Error: Send block data returned empty string/timed out", bitforce->device_id); + if (amount == 0 || !buf[0]) { + applog(LOG_ERR, "%s%i: Error: Send block data returned empty string/timed out", + bitforce->drv->name, bitforce->device_id); return false; } - if (unlikely(strncasecmp(pdevbuf, "OK", 2))) { - applog(LOG_ERR, "BFL%i: Error: Send block data reports: %s", bitforce->device_id, pdevbuf); + if (unlikely(strncasecmp(buf, "OK", 2))) { + applog(LOG_ERR, "%s%i: Error: Send block data reports: %s", + bitforce->drv->name, bitforce->device_id, buf); return false; } @@ -521,53 +555,52 @@ re_send: static int64_t bitforce_get_result(struct thr_info *thr, struct work *work) { struct cgpu_info *bitforce = thr->cgpu; - int fdDev = bitforce->device_fd; unsigned int delay_time_ms; struct timeval elapsed; struct timeval now; - char pdevbuf[0x100]; + char buf[BITFORCE_BUFSIZ+1]; + int amount; char *pnoncebuf; uint32_t nonce; - if (!fdDev) - return -1; - while (1) { if (unlikely(thr->work_restart)) return 0; mutex_lock(&bitforce->device_mutex); - BFwrite(fdDev, "ZFX", 3); - pdevbuf[0] = '\0'; - BFgets(pdevbuf, sizeof(pdevbuf), fdDev); + usb_write(bitforce, BITFORCE_WORKSTATUS, BITFORCE_WORKSTATUS_LEN, &amount, C_REQUESTWORKSTATUS); + usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETWORKSTATUS); mutex_unlock(&bitforce->device_mutex); gettimeofday(&now, NULL); timersub(&now, &bitforce->work_start_tv, &elapsed); if (elapsed.tv_sec >= BITFORCE_LONG_TIMEOUT_S) { - applog(LOG_ERR, "BFL%i: took %dms - longer than %dms", bitforce->device_id, + applog(LOG_ERR, "%s%i: took %dms - longer than %dms", + bitforce->drv->name, bitforce->device_id, tv_to_ms(elapsed), BITFORCE_LONG_TIMEOUT_MS); return 0; } - if (pdevbuf[0] && strncasecmp(pdevbuf, "B", 1)) /* BFL does not respond during throttling */ + if (amount > 0 && buf[0] && strncasecmp(buf, "B", 1)) /* BFL does not respond during throttling */ break; /* if BFL is throttling, no point checking so quickly */ - delay_time_ms = (pdevbuf[0] ? BITFORCE_CHECK_INTERVAL_MS : 2 * WORK_CHECK_INTERVAL_MS); + delay_time_ms = (buf[0] ? BITFORCE_CHECK_INTERVAL_MS : 2 * WORK_CHECK_INTERVAL_MS); nmsleep(delay_time_ms); bitforce->wait_ms += delay_time_ms; } if (elapsed.tv_sec > BITFORCE_TIMEOUT_S) { - applog(LOG_ERR, "BFL%i: took %dms - longer than %dms", bitforce->device_id, + applog(LOG_ERR, "%s%i: took %dms - longer than %dms", + bitforce->drv->name, bitforce->device_id, tv_to_ms(elapsed), BITFORCE_TIMEOUT_MS); dev_error(bitforce, REASON_DEV_OVER_HEAT); - if (!pdevbuf[0]) /* Only return if we got nothing after timeout - there still may be results */ + /* Only return if we got nothing after timeout - there still may be results */ + if (amount == 0) return 0; - } else if (!strncasecmp(pdevbuf, "N", 1)) {/* Hashing complete (NONCE-FOUND or NO-NONCE) */ + } else if (!strncasecmp(buf, BITFORCE_EITHER, BITFORCE_EITHER_LEN)) { /* Simple timing adjustment. Allow a few polls to cope with * OS timer delays being variably reliable. wait_ms will * always equal sleep_ms when we've waited greater than or @@ -584,26 +617,31 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work) } if (delay_time_ms != bitforce->sleep_ms) - applog(LOG_DEBUG, "BFL%i: Wait time changed to: %d, waited %u", bitforce->device_id, bitforce->sleep_ms, bitforce->wait_ms); + applog(LOG_DEBUG, "%s%i: Wait time changed to: %d, waited %u", + bitforce->drv->name, bitforce->device_id, + bitforce->sleep_ms, bitforce->wait_ms); /* Work out the average time taken. Float for calculation, uint for display */ bitforce->avg_wait_f += (tv_to_ms(elapsed) - bitforce->avg_wait_f) / TIME_AVG_CONSTANT; bitforce->avg_wait_d = (unsigned int) (bitforce->avg_wait_f + 0.5); } - applog(LOG_DEBUG, "BFL%i: waited %dms until %s", bitforce->device_id, bitforce->wait_ms, pdevbuf); - if (!strncasecmp(&pdevbuf[2], "-", 1)) + applog(LOG_DEBUG, "%s%i: waited %dms until %s", + bitforce->drv->name, bitforce->device_id, + bitforce->wait_ms, buf); + if (!strncasecmp(buf, BITFORCE_NO_NONCE, BITFORCE_NO_NONCE_MATCH)) return bitforce->nonces; /* No valid nonce found */ - else if (!strncasecmp(pdevbuf, "I", 1)) + else if (!strncasecmp(buf, BITFORCE_IDLE, BITFORCE_IDLE_MATCH)) return 0; /* Device idle */ - else if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) { + else if (strncasecmp(buf, BITFORCE_NONCE, BITFORCE_NONCE_LEN)) { bitforce->hw_errors++; - applog(LOG_WARNING, "BFL%i: Error: Get result reports: %s", bitforce->device_id, pdevbuf); - bitforce_clear_buffer(bitforce); + applog(LOG_WARNING, "%s%i: Error: Get result reports: %s", + bitforce->drv->name, bitforce->device_id, buf); + bitforce_initialise(bitforce, true); return 0; } - pnoncebuf = &pdevbuf[12]; + pnoncebuf = &buf[12]; while (1) { hex2bin((void*)&nonce, pnoncebuf, 4); @@ -612,7 +650,8 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work) #endif if (unlikely(bitforce->nonce_range && (nonce >= work->blk.nonce || (work->blk.nonce > 0 && nonce < work->blk.nonce - bitforce->nonces - 1)))) { - applog(LOG_WARNING, "BFL%i: Disabling broken nonce range support", bitforce->device_id); + applog(LOG_WARNING, "%s%i: Disabling broken nonce range support", + bitforce->drv->name, bitforce->device_id); bitforce->nonce_range = false; work->blk.nonce = 0xffffffff; bitforce->sleep_ms *= 5; @@ -628,19 +667,16 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work) return bitforce->nonces; } -static void bitforce_shutdown(struct thr_info *thr) +static void bitforce_shutdown(__maybe_unused struct thr_info *thr) { - struct cgpu_info *bitforce = thr->cgpu; - - BFclose(bitforce->device_fd); - bitforce->device_fd = 0; +// struct cgpu_info *bitforce = thr->cgpu; } static void biforce_thread_enable(struct thr_info *thr) { struct cgpu_info *bitforce = thr->cgpu; - bitforce_init(bitforce); + bitforce_initialise(bitforce, true); } static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce) @@ -649,6 +685,10 @@ static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_ bool send_ret; int64_t ret; + // Device is gone + if (bitforce->usbinfo.nodev) + return -1; + send_ret = bitforce_send_work(thr, work); if (!restart_wait(bitforce->sleep_ms)) @@ -665,11 +705,11 @@ static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_ if (ret == -1) { ret = 0; - applog(LOG_ERR, "BFL%i: Comms error", bitforce->device_id); + applog(LOG_ERR, "%s%i: Comms error", bitforce->drv->name, bitforce->device_id); dev_error(bitforce, REASON_DEV_COMMS_ERROR); bitforce->hw_errors++; /* empty read buffer */ - bitforce_clear_buffer(bitforce); + bitforce_initialise(bitforce, true); } return ret; } @@ -692,7 +732,8 @@ static bool bitforce_thread_init(struct thr_info *thr) /* Pause each new thread at least 100ms between initialising * so the devices aren't making calls all at the same time. */ wait = thr->id * MAX_START_DELAY_MS; - applog(LOG_DEBUG, "BFL%i: Delaying start by %dms", bitforce->device_id, wait / 1000); + applog(LOG_DEBUG, "%s%d: Delaying start by %dms", + bitforce->drv->name, bitforce->device_id, wait / 1000); nmsleep(wait); return true; @@ -712,12 +753,12 @@ static struct api_data *bitforce_api_stats(struct cgpu_info *cgpu) return root; } -struct device_api bitforce_api = { - .dname = "bitforce", +struct device_drv bitforce_drv = { + .drv_id = DRIVER_BITFORCE, + .dname = "BitForce", .name = "BFL", - .api_detect = bitforce_detect, + .drv_detect = bitforce_detect, .get_api_stats = bitforce_api_stats, - .reinit_device = bitforce_init, .get_statline_before = get_bitforce_statline_before, .get_stats = bitforce_get_stats, .identify_device = bitforce_identify, diff --git a/driver-cpu.c b/driver-cpu.c index 75d9a5a4..a8b7a56a 100644 --- a/driver-cpu.c +++ b/driver-cpu.c @@ -758,7 +758,7 @@ static void cpu_detect() struct cgpu_info *cgpu; cgpu = &cpus[i]; - cgpu->api = &cpu_api; + cgpu->drv = &cpu_drv; cgpu->deven = DEV_ENABLED; cgpu->threads = 1; cgpu->kname = algo_names[opt_algo]; @@ -768,7 +768,7 @@ static void cpu_detect() static void reinit_cpu_device(struct cgpu_info *cpu) { - tq_push(thr_info[cpur_thr_id].q, cpu); + tq_push(control_thr[cpur_thr_id].q, cpu); } static bool cpu_thread_prepare(struct thr_info *thr) @@ -843,10 +843,11 @@ CPUSearch: return last_nonce - first_nonce + 1; } -struct device_api cpu_api = { +struct device_drv cpu_drv = { + .drv_id = DRIVER_CPU, .dname = "cpu", .name = "CPU", - .api_detect = cpu_detect, + .drv_detect = cpu_detect, .reinit_device = reinit_cpu_device, .thread_prepare = cpu_thread_prepare, .can_limit_work = cpu_can_limit_work, diff --git a/driver-cpu.h b/driver-cpu.h index e4b44527..361ae5d8 100644 --- a/driver-cpu.h +++ b/driver-cpu.h @@ -53,7 +53,7 @@ enum sha256_algos { extern const char *algo_names[]; extern bool opt_usecpu; -extern struct device_api cpu_api; +extern struct device_drv cpu_drv; extern char *set_algo(const char *arg, enum sha256_algos *algo); extern void show_algo(char buf[OPT_SHOW_LEN], const enum sha256_algos *algo); diff --git a/driver-icarus.c b/driver-icarus.c index c979a681..9a99e866 100644 --- a/driver-icarus.c +++ b/driver-icarus.c @@ -208,7 +208,7 @@ static struct ICARUS_INFO **icarus_info; // static int option_offset = -1; -struct device_api icarus_api; +struct device_drv icarus_drv; static void rev(unsigned char *s, size_t l) { @@ -571,7 +571,7 @@ static bool icarus_detect_one(const char *devpath) /* We have a real Icarus! */ struct cgpu_info *icarus; icarus = calloc(1, sizeof(struct cgpu_info)); - icarus->api = &icarus_api; + icarus->drv = &icarus_drv; icarus->device_path = strdup(devpath); icarus->device_fd = -1; icarus->threads = 1; @@ -609,7 +609,7 @@ static bool icarus_detect_one(const char *devpath) static void icarus_detect() { - serial_detect(&icarus_api, icarus_detect_one); + serial_detect(&icarus_drv, icarus_detect_one); } static bool icarus_prepare(struct thr_info *thr) @@ -668,7 +668,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, icarus = thr->cgpu; if (icarus->device_fd == -1) if (!icarus_prepare(thr)) { - applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id); + applog(LOG_ERR, "%s%i: Comms error", icarus->drv->name, icarus->device_id); dev_error(icarus, REASON_DEV_COMMS_ERROR); // fail the device if the reopen attempt fails @@ -688,7 +688,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, ret = icarus_write(fd, ob_bin, sizeof(ob_bin)); if (ret) { do_icarus_close(thr); - applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id); + applog(LOG_ERR, "%s%i: Comms error", icarus->drv->name, icarus->device_id); dev_error(icarus, REASON_DEV_COMMS_ERROR); return 0; /* This should never happen */ } @@ -708,7 +708,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, ret = icarus_gets(nonce_bin, fd, &tv_finish, thr, info->read_count); if (ret == ICA_GETS_ERROR) { do_icarus_close(thr); - applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id); + applog(LOG_ERR, "%s%i: Comms error", icarus->drv->name, icarus->device_id); dev_error(icarus, REASON_DEV_COMMS_ERROR); return 0; } @@ -900,10 +900,11 @@ static void icarus_shutdown(struct thr_info *thr) do_icarus_close(thr); } -struct device_api icarus_api = { - .dname = "icarus", +struct device_drv icarus_drv = { + .drv_id = DRIVER_ICARUS, + .dname = "Icarus", .name = "ICA", - .api_detect = icarus_detect, + .drv_detect = icarus_detect, .get_api_stats = icarus_api_stats, .thread_prepare = icarus_prepare, .scanhash = icarus_scanhash, diff --git a/driver-modminer.c b/driver-modminer.c index c48a51bc..10d42a3b 100644 --- a/driver-modminer.c +++ b/driver-modminer.c @@ -1,5 +1,5 @@ /* - * Copyright 2012 Andrew Smith + * Copyright 2012-2013 Andrew Smith * Copyright 2012 Luke Dashjr * * This program is free software; you can redistribute it and/or modify it @@ -87,7 +87,7 @@ // Limit when reducing shares_to_good #define MODMINER_MIN_BACK 12 -struct device_api modminer_api; +struct device_drv modminer_drv; // 45 noops sent when detecting, in case the device was left in "start job" reading static const char NOOP[] = MODMINER_PING "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; @@ -100,15 +100,15 @@ static void do_ping(struct cgpu_info *modminer) // Don't care if it fails err = usb_write(modminer, (char *)NOOP, sizeof(NOOP)-1, &amount, C_PING); applog(LOG_DEBUG, "%s%u: flush noop got %d err %d", - modminer->api->name, modminer->fpgaid, amount, err); + modminer->drv->name, modminer->fpgaid, amount, err); // Clear any outstanding data while ((err = usb_read(modminer, buf, sizeof(buf)-1, &amount, C_CLEAR)) == 0 && amount > 0) applog(LOG_DEBUG, "%s%u: clear got %d", - modminer->api->name, modminer->fpgaid, amount); + modminer->drv->name, modminer->fpgaid, amount); applog(LOG_DEBUG, "%s%u: final clear got %d err %d", - modminer->api->name, modminer->fpgaid, amount, err); + modminer->drv->name, modminer->fpgaid, amount, err); } static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devices *found) @@ -121,42 +121,57 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic struct cgpu_info *modminer = NULL; modminer = calloc(1, sizeof(*modminer)); - modminer->api = &modminer_api; + modminer->drv = &modminer_drv; modminer->modminer_mutex = calloc(1, sizeof(*(modminer->modminer_mutex))); mutex_init(modminer->modminer_mutex); modminer->fpgaid = (char)0; - if (!usb_init(modminer, dev, found)) + if (!usb_init(modminer, dev, found)) { + applog(LOG_ERR, "%s detect (%d:%d) failed to initialise (incorrect device?)", + modminer->drv->dname, + (int)(modminer->usbinfo.bus_number), + (int)(modminer->usbinfo.device_address)); goto shin; + } + + sprintf(devpath, "%d:%d", + (int)(modminer->usbinfo.bus_number), + (int)(modminer->usbinfo.device_address)); do_ping(modminer); if ((err = usb_write(modminer, MODMINER_GET_VERSION, 1, &amount, C_REQUESTVERSION)) < 0 || amount != 1) { - applog(LOG_ERR, "ModMiner detect: send version request failed (%d:%d)", amount, err); + applog(LOG_ERR, "%s detect (%s) send version request failed (%d:%d)", + modminer->drv->dname, devpath, amount, err); goto unshin; } if ((err = usb_read(modminer, buf, sizeof(buf)-1, &amount, C_GETVERSION)) < 0 || amount < 1) { if (err < 0) - applog(LOG_ERR, "ModMiner detect: no version reply (%d)", err); + applog(LOG_ERR, "%s detect (%s) no version reply (%d)", + modminer->drv->dname, devpath, err); else - applog(LOG_ERR, "ModMiner detect: empty version reply (%d)", amount); + applog(LOG_ERR, "%s detect (%s) empty version reply (%d)", + modminer->drv->dname, devpath, amount); - applog(LOG_DEBUG, "ModMiner detect: check the firmware"); + applog(LOG_DEBUG, "%s detect (%s) check the firmware", + modminer->drv->dname, devpath); goto unshin; } buf[amount] = '\0'; devname = strdup(buf); - applog(LOG_DEBUG, "ModMiner identified as: %s", devname); + applog(LOG_DEBUG, "%s (%s) identified as: %s", modminer->drv->dname, devpath, devname); if ((err = usb_write(modminer, MODMINER_FPGA_COUNT, 1, &amount, C_REQUESTFPGACOUNT) < 0 || amount != 1)) { - applog(LOG_ERR, "ModMiner detect: FPGA count request failed (%d:%d)", amount, err); + applog(LOG_ERR, "%s detect (%s) FPGA count request failed (%d:%d)", + modminer->drv->dname, devpath, amount, err); goto unshin; } if ((err = usb_read(modminer, buf, 1, &amount, C_GETFPGACOUNT)) < 0 || amount != 1) { - applog(LOG_ERR, "ModMiner detect: no FPGA count reply (%d:%d)", amount, err); + applog(LOG_ERR, "%s detect (%s) no FPGA count reply (%d:%d)", + modminer->drv->dname, devpath, amount, err); goto unshin; } @@ -164,16 +179,19 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic // can detect with modminer->cgusb->serial ? if (buf[0] == 0) { - applog(LOG_ERR, "ModMiner detect: zero FPGA count from %s", devname); + applog(LOG_ERR, "%s detect (%s) zero FPGA count from %s", + modminer->drv->dname, devpath, devname); goto unshin; } if (buf[0] < 1 || buf[0] > 4) { - applog(LOG_ERR, "ModMiner detect: invalid FPGA count (%u) from %s", buf[0], devname); + applog(LOG_ERR, "%s detect (%s) invalid FPGA count (%u) from %s", + modminer->drv->dname, devpath, buf[0], devname); goto unshin; } - applog(LOG_DEBUG, "ModMiner %s has %u FPGAs", devname, buf[0]); + applog(LOG_DEBUG, "%s (%s) %s has %u FPGAs", + modminer->drv->dname, devpath, devname, buf[0]); modminer->name = devname; @@ -182,19 +200,21 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic for (i = 0; i < buf[0]; i++) { struct cgpu_info *tmp = calloc(1, sizeof(*tmp)); - tmp->api = modminer->api; + tmp->drv = copy_drv(modminer->drv); tmp->name = devname; sprintf(devpath, "%d:%d:%d", - (int)(modminer->usbdev->bus_number), - (int)(modminer->usbdev->device_address), + (int)(modminer->usbinfo.bus_number), + (int)(modminer->usbinfo.device_address), i); tmp->device_path = strdup(devpath); tmp->usbdev = modminer->usbdev; + tmp->usbinfo.bus_number = modminer->usbinfo.bus_number; + tmp->usbinfo.device_address = modminer->usbinfo.device_address; // Only the first copy gets the already used stats if (!added) - tmp->usbstat = modminer->usbstat; + tmp->usbinfo.usbstat = modminer->usbinfo.usbstat; tmp->fpgaid = (char)i; tmp->modminer_mutex = modminer->modminer_mutex; tmp->deven = DEV_ENABLED; @@ -202,6 +222,8 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic if (!add_cgpu(tmp)) { free(tmp->device_path); + if (tmp->drv->copy) + free(tmp->drv); free(tmp); goto unshin; } @@ -211,6 +233,9 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic added = true; } + if (modminer->drv->copy) + free(modminer->drv); + free(modminer); return true; @@ -223,6 +248,9 @@ shin: if (!added) free(modminer->modminer_mutex); + if (modminer->drv->copy) + free(modminer->drv); + free(modminer); if (added) @@ -233,7 +261,7 @@ shin: static void modminer_detect() { - usb_detect(&modminer_api, modminer_detect_one); + usb_detect(&modminer_drv, modminer_detect_one); } static bool get_expect(struct cgpu_info *modminer, FILE *f, char c) @@ -242,13 +270,13 @@ static bool get_expect(struct cgpu_info *modminer, FILE *f, char c) if (fread(&buf, 1, 1, f) != 1) { applog(LOG_ERR, "%s%u: Error (%d) reading bitstream (%c)", - modminer->api->name, modminer->device_id, errno, c); + modminer->drv->name, modminer->device_id, errno, c); return false; } if (buf != c) { - applog(LOG_ERR, "%s%u: firmware code mismatch (%c)", - modminer->api->name, modminer->device_id, c); + applog(LOG_ERR, "%s%u: bitstream code mismatch (%c)", + modminer->drv->name, modminer->device_id, c); return false; } @@ -262,7 +290,7 @@ static bool get_info(struct cgpu_info *modminer, FILE *f, char *buf, int bufsiz, if (fread(siz, 2, 1, f) != 1) { applog(LOG_ERR, "%s%u: Error (%d) reading bitstream '%s' len", - modminer->api->name, modminer->device_id, errno, name); + modminer->drv->name, modminer->device_id, errno, name); return false; } @@ -270,13 +298,13 @@ static bool get_info(struct cgpu_info *modminer, FILE *f, char *buf, int bufsiz, if (len >= bufsiz) { applog(LOG_ERR, "%s%u: Bitstream '%s' len too large (%d)", - modminer->api->name, modminer->device_id, name, len); + modminer->drv->name, modminer->device_id, name, len); return false; } if (fread(buf, len, 1, f) != 1) { applog(LOG_ERR, "%s%u: Error (%d) reading bitstream '%s'", errno, - modminer->api->name, modminer->device_id, errno, name); + modminer->drv->name, modminer->device_id, errno, name); return false; } @@ -302,7 +330,7 @@ static bool get_status_timeout(struct cgpu_info *modminer, char *msg, unsigned i mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error (%d:%d) getting %s reply", - modminer->api->name, modminer->device_id, amount, err, msg); + modminer->drv->name, modminer->device_id, amount, err, msg); return false; } @@ -311,7 +339,7 @@ static bool get_status_timeout(struct cgpu_info *modminer, char *msg, unsigned i mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error, invalid %s reply (was %d should be 1)", - modminer->api->name, modminer->device_id, msg, buf[0]); + modminer->drv->name, modminer->device_id, msg, buf[0]); return false; } @@ -343,7 +371,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error (%d) opening bitstream file %s", - modminer->api->name, modminer->device_id, errno, bsfile); + modminer->drv->name, modminer->device_id, errno, bsfile); return false; } @@ -352,7 +380,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error (%d) reading bitstream magic", - modminer->api->name, modminer->device_id, errno); + modminer->drv->name, modminer->device_id, errno); goto dame; } @@ -361,7 +389,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: bitstream has incorrect magic (%u,%u) instead of (%u,%u)", - modminer->api->name, modminer->device_id, + modminer->drv->name, modminer->device_id, buf[0], buf[1], BITSTREAM_MAGIC_0, BITSTREAM_MAGIC_1); @@ -372,7 +400,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error (%d) bitstream seek failed", - modminer->api->name, modminer->device_id, errno); + modminer->drv->name, modminer->device_id, errno); goto dame; } @@ -384,7 +412,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) goto undame; applog(LOG_DEBUG, "%s%u: bitstream file '%s' info:", - modminer->api->name, modminer->device_id, bsfile); + modminer->drv->name, modminer->device_id, bsfile); applog(LOG_DEBUG, " Design name: '%s'", buf); @@ -399,7 +427,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Bad usercode in bitstream file", - modminer->api->name, modminer->device_id); + modminer->drv->name, modminer->device_id); goto dame; } @@ -408,7 +436,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: bitstream doesn't support user code", - modminer->api->name, modminer->device_id); + modminer->drv->name, modminer->device_id); goto dame; } @@ -446,7 +474,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error (%d) reading bitstream data len", - modminer->api->name, modminer->device_id, errno); + modminer->drv->name, modminer->device_id, errno); goto dame; } @@ -460,7 +488,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) *ptr = '\0'; applog(LOG_WARNING, "%s%u: Programming all FPGA on %s ... Mining will not start until complete", - modminer->api->name, modminer->device_id, devmsg); + modminer->drv->name, modminer->device_id, devmsg); buf[0] = MODMINER_PROGRAM; buf[1] = fpgaid; @@ -473,7 +501,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Program init failed (%d:%d)", - modminer->api->name, modminer->device_id, amount, err); + modminer->drv->name, modminer->device_id, amount, err); goto dame; } @@ -492,7 +520,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: bitstream file read error %d (%d bytes left)", - modminer->api->name, modminer->device_id, errno, len); + modminer->drv->name, modminer->device_id, errno, len); goto dame; } @@ -507,7 +535,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) if (opt_debug) applog(LOG_DEBUG, "%s%u: Program timeout (%d:%d) sent %d tries %d", - modminer->api->name, modminer->device_id, + modminer->drv->name, modminer->device_id, amount, err, remaining, tries); if (!get_status(modminer, "write status", C_PROGRAMSTATUS2)) @@ -517,7 +545,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Program failed (%d:%d) sent %d", - modminer->api->name, modminer->device_id, amount, err, remaining); + modminer->drv->name, modminer->device_id, amount, err, remaining); goto dame; } @@ -532,7 +560,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) if (upto >= nextmsg) { applog(LOG_WARNING, "%s%u: Programming %.1f%% (%d out of %d)", - modminer->api->name, modminer->device_id, upto*100, (totlen - len), totlen); + modminer->drv->name, modminer->device_id, upto*100, (totlen - len), totlen); nextmsg += 0.1; } @@ -542,7 +570,7 @@ static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer) goto undame; applog(LOG_WARNING, "%s%u: Programming completed for all FPGA on %s", - modminer->api->name, modminer->device_id, devmsg); + modminer->drv->name, modminer->device_id, devmsg); // Give it a 2/3s delay after programming nmsleep(666); @@ -567,8 +595,6 @@ static bool modminer_fpga_prepare(struct thr_info *thr) struct modminer_fpga_state *state; state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state)); - state->next_work_cmd[0] = MODMINER_SEND_WORK; - state->next_work_cmd[1] = modminer->fpgaid; state->shares_to_good = MODMINER_EARLY_UP; state->overheated = false; @@ -599,6 +625,7 @@ static bool modminer_fpga_prepare(struct thr_info *thr) * * N.B. clock must always be a multiple of 2 */ +static const char *clocknodev = "clock failed - no device"; static const char *clockoldwork = "clock already changed for this work"; static const char *clocktoolow = "clock too low"; static const char *clocktoohi = "clock too high"; @@ -612,6 +639,10 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te unsigned char cmd[6], buf[1]; int err, amount; + // Device is gone + if (modminer->usbinfo.nodev) + return clocknodev; + // Only do once if multiple shares per work or multiple reasons if (!state->new_work && !force) return clockoldwork; @@ -653,7 +684,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error writing set clock speed (%d:%d)", - modminer->api->name, modminer->device_id, amount, err); + modminer->drv->name, modminer->device_id, amount, err); return clocksetfail; } @@ -662,7 +693,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error reading set clock speed (%d:%d)", - modminer->api->name, modminer->device_id, amount, err); + modminer->drv->name, modminer->device_id, amount, err); return clockreplyfail; } @@ -670,7 +701,7 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te mutex_unlock(modminer->modminer_mutex); applog(LOG_WARNING, "%s%u: Set clock speed %sto %u", - modminer->api->name, modminer->device_id, + modminer->drv->name, modminer->device_id, (delta < 0) ? "down " : (delta > 0 ? "up " : ""), modminer->clock); @@ -691,7 +722,7 @@ static bool modminer_fpga_init(struct thr_info *thr) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error requesting USER code (%d:%d)", - modminer->api->name, modminer->device_id, amount, err); + modminer->drv->name, modminer->device_id, amount, err); return false; } @@ -700,14 +731,14 @@ static bool modminer_fpga_init(struct thr_info *thr) mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Error reading USER code (%d:%d)", - modminer->api->name, modminer->device_id, amount, err); + modminer->drv->name, modminer->device_id, amount, err); return false; } if (memcmp(buf, BISTREAM_USER_ID, 4)) { applog(LOG_ERR, "%s%u: FPGA not programmed", - modminer->api->name, modminer->device_id); + modminer->drv->name, modminer->device_id); if (!modminer_fpga_upload_bitstream(modminer)) return false; @@ -717,7 +748,7 @@ static bool modminer_fpga_init(struct thr_info *thr) mutex_unlock(modminer->modminer_mutex); applog(LOG_DEBUG, "%s%u: FPGA is already programmed :)", - modminer->api->name, modminer->device_id); + modminer->drv->name, modminer->device_id); } modminer->clock = MODMINER_DEF_CLOCK; @@ -740,24 +771,19 @@ static void get_modminer_statline_before(char *buf, struct cgpu_info *modminer) strcat(buf, info); } -static bool modminer_prepare_next_work(struct modminer_fpga_state *state, struct work *work) -{ - char *midstate = state->next_work_cmd + 2; - char *taildata = midstate + 32; - if (!(memcmp(midstate, work->midstate, 32) || memcmp(taildata, work->data + 64, 12))) - return false; - memcpy(midstate, work->midstate, 32); - memcpy(taildata, work->data + 64, 12); - return true; -} - -static bool modminer_start_work(struct thr_info *thr) +static bool modminer_start_work(struct thr_info *thr, struct work *work) { struct cgpu_info *modminer = thr->cgpu; struct modminer_fpga_state *state = thr->cgpu_data; int err, amount; + char cmd[48]; bool sta; + cmd[0] = MODMINER_SEND_WORK; + cmd[1] = modminer->fpgaid; + memcpy(&cmd[2], work->midstate, 32); + memcpy(&cmd[34], work->data + 64, 12); + if (state->first_work.tv_sec == 0) gettimeofday(&state->first_work, NULL); @@ -766,20 +792,16 @@ static bool modminer_start_work(struct thr_info *thr) mutex_lock(modminer->modminer_mutex); - if ((err = usb_write(modminer, (char *)(state->next_work_cmd), 46, &amount, C_SENDWORK)) < 0 || amount != 46) { -// TODO: err = LIBUSB_ERROR_NO_DEVICE means the MMQ disappeared -// - need to delete it and rescan for it? (after a delay?) -// but check all (4) disappeared + if ((err = usb_write(modminer, cmd, 46, &amount, C_SENDWORK)) < 0 || amount != 46) { mutex_unlock(modminer->modminer_mutex); applog(LOG_ERR, "%s%u: Start work failed (%d:%d)", - modminer->api->name, modminer->device_id, amount, err); + modminer->drv->name, modminer->device_id, amount, err); return false; } gettimeofday(&state->tv_workstart, NULL); - state->hashes = 0; sta = get_status(modminer, "start work", C_SENDWORKSTATUS); @@ -799,6 +821,10 @@ static void check_temperature(struct thr_info *thr) int tbytes, tamount; int amount; + // Device is gone + if (modminer->usbinfo.nodev) + return; + if (state->one_byte_temp) { cmd[0] = MODMINER_TEMP1; tbytes = 1; @@ -827,14 +853,14 @@ static void check_temperature(struct thr_info *thr) if (modminer->temp < MODMINER_RECOVER_TEMP) { state->overheated = false; applog(LOG_WARNING, "%s%u: Recovered, temp less than (%.1f) now %.3f", - modminer->api->name, modminer->device_id, + modminer->drv->name, modminer->device_id, MODMINER_RECOVER_TEMP, modminer->temp); } } else if (modminer->temp >= MODMINER_OVERHEAT_TEMP) { if (modminer->temp >= MODMINER_CUTOFF_TEMP) { applog(LOG_WARNING, "%s%u: Hit thermal cutoff limit! (%.1f) at %.3f", - modminer->api->name, modminer->device_id, + modminer->drv->name, modminer->device_id, MODMINER_CUTOFF_TEMP, modminer->temp); modminer_delta_clock(thr, MODMINER_CLOCK_CUTOFF, true, false); @@ -842,7 +868,7 @@ static void check_temperature(struct thr_info *thr) dev_error(modminer, REASON_DEV_THERMAL_CUTOFF); } else { applog(LOG_WARNING, "%s%u: Overheat limit (%.1f) reached %.3f", - modminer->api->name, modminer->device_id, + modminer->drv->name, modminer->device_id, MODMINER_OVERHEAT_TEMP, modminer->temp); // If it's defined to be 0 then don't call modminer_delta_clock() @@ -869,11 +895,10 @@ static const double processtime = 17.0; // 160Mhz is 26.84 - when overheated ensure we don't throw away shares static const double overheattime = 26.9; -static uint64_t modminer_process_results(struct thr_info *thr) +static uint64_t modminer_process_results(struct thr_info *thr, struct work *work) { struct cgpu_info *modminer = thr->cgpu; struct modminer_fpga_state *state = thr->cgpu_data; - struct work *work = &state->running_work; struct timeval now; char cmd[2]; uint32_t nonce; @@ -883,6 +908,10 @@ static uint64_t modminer_process_results(struct thr_info *thr) double timeout; int temploop; + // Device is gone + if (modminer->usbinfo.nodev) + return -1; + // If we are overheated it will just keep checking for results // since we can't stop the work // The next work will not start until the temp drops @@ -893,23 +922,20 @@ static uint64_t modminer_process_results(struct thr_info *thr) timeoutloop = 0; temploop = 0; - while (1) { + while (0x80085) { mutex_lock(modminer->modminer_mutex); if ((err = usb_write(modminer, cmd, 2, &amount, C_REQUESTWORKSTATUS)) < 0 || amount != 2) { -// TODO: err = LIBUSB_ERROR_NO_DEVICE means the MMQ disappeared -// - need to delete it and rescan for it? (after a delay?) -// but check all (4) disappeared mutex_unlock(modminer->modminer_mutex); // timeoutloop never resets so the timeouts can't // accumulate much during a single item of work - if (err == LIBUSB_ERROR_TIMEOUT && ++timeoutloop < 10) { + if (err == LIBUSB_ERROR_TIMEOUT && ++timeoutloop < 5) { state->timeout_fail++; goto tryagain; } applog(LOG_ERR, "%s%u: Error sending (get nonce) (%d:%d)", - modminer->api->name, modminer->device_id, amount, err); + modminer->drv->name, modminer->device_id, amount, err); return -1; } @@ -936,7 +962,7 @@ static uint64_t modminer_process_results(struct thr_info *thr) } applog(LOG_ERR, "%s%u: Error reading (get nonce) (%d:%d)", - modminer->api->name, modminer->device_id, amount+amount2, err); + modminer->drv->name, modminer->device_id, amount+amount2, err); } if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) { @@ -975,7 +1001,7 @@ static uint64_t modminer_process_results(struct thr_info *thr) if (state->death_stage_one) { modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true); applog(LOG_ERR, "%s%u: DEATH clock down", - modminer->api->name, modminer->device_id); + modminer->drv->name, modminer->device_id); // reset the death info and DISABLE it state->last_nonce.tv_sec = 0; @@ -985,7 +1011,7 @@ static uint64_t modminer_process_results(struct thr_info *thr) } else { modminer_delta_clock(thr, MODMINER_CLOCK_DEAD, false, true); applog(LOG_ERR, "%s%u: death clock down", - modminer->api->name, modminer->device_id); + modminer->drv->name, modminer->device_id); state->death_stage_one = true; } @@ -998,8 +1024,8 @@ tryagain: break; if (state->overheated == true) { - // don't check every time - if (++temploop > 30) { + // don't check every time (every ~1/2 sec) + if (++temploop > 4) { check_temperature(thr); temploop = 0; } @@ -1015,7 +1041,8 @@ tryagain: if (tdiff(&now, &state->tv_workstart) > timeout) break; - nmsleep(10); + // 1/10th sec to lower CPU usage + nmsleep(100); if (work_restart(thr)) break; } @@ -1029,35 +1056,37 @@ tryagain: // Overheat will complete the nonce range if (hashes > 0xffffffff) hashes = 0xffffffff; - else - if (hashes <= state->hashes) - hashes = 1; - else - hashes -= state->hashes; - state->hashes += hashes; + + work->blk.nonce = 0xffffffff; + return hashes; } static int64_t modminer_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce) { struct modminer_fpga_state *state = thr->cgpu_data; - int64_t hashes = 0; - bool startwork; struct timeval tv1, tv2; + int64_t hashes; + + // Device is gone + if (thr->cgpu->usbinfo.nodev) + return -1; // Don't start new work if overheated if (state->overheated == true) { gettimeofday(&tv1, NULL); - if (state->work_running) - state->work_running = false; while (state->overheated == true) { check_temperature(thr); + // Device is gone + if (thr->cgpu->usbinfo.nodev) + return -1; + if (state->overheated == true) { gettimeofday(&tv2, NULL); - // give up on this work item + // give up on this work item after 30s if (work_restart(thr) || tdiff(&tv2, &tv1) > 30) return 0; @@ -1067,27 +1096,13 @@ static int64_t modminer_scanhash(struct thr_info *thr, struct work *work, int64_ } } - startwork = modminer_prepare_next_work(state, work); - if (state->work_running) { - hashes = modminer_process_results(thr); - if (hashes == -1) - return hashes; - - if (work_restart(thr)) { - state->work_running = false; - return 0; - } - } else - state->work_running = true; + if (!modminer_start_work(thr, work)) + return -1; - if (startwork) { - if (!modminer_start_work(thr)) - return -1; - __copy_work(&state->running_work, work); - } + hashes = modminer_process_results(thr, work); + if (hashes == -1) + return hashes; - // This is intentionally early - work->blk.nonce += hashes; return hashes; } @@ -1141,10 +1156,11 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char return replybuf; } -struct device_api modminer_api = { - .dname = "modminer", +struct device_drv modminer_drv = { + .drv_id = DRIVER_MODMINER, + .dname = "ModMiner", .name = "MMQ", - .api_detect = modminer_detect, + .drv_detect = modminer_detect, .get_statline_before = get_modminer_statline_before, .set_device = modminer_set_device, .thread_prepare = modminer_fpga_prepare, diff --git a/driver-opencl.c b/driver-opencl.c index 01e70198..8580b221 100644 --- a/driver-opencl.c +++ b/driver-opencl.c @@ -50,24 +50,24 @@ extern int gpur_thr_id; extern bool opt_noadl; extern bool have_opencl; - - extern void *miner_thread(void *userdata); extern int dev_from_id(int thr_id); extern void tailsprintf(char *f, const char *fmt, ...); extern void wlog(const char *f, ...); extern void decay_time(double *f, double fadd); - /**********************************************/ +#ifdef HAVE_OPENCL +struct device_drv opencl_drv; +#endif + #ifdef HAVE_ADL extern float gpu_temp(int gpu); extern int gpu_fanspeed(int gpu); extern int gpu_fanpercent(int gpu); #endif - #ifdef HAVE_OPENCL char *set_vector(char *arg) { @@ -591,28 +591,20 @@ char *set_intensity(char *arg) return NULL; } -#endif - - -#ifdef HAVE_OPENCL -struct device_api opencl_api; char *print_ndevs_and_exit(int *ndevs) { opt_log_output = true; - opencl_api.api_detect(); + opencl_drv.drv_detect(); clear_adl(*ndevs); applog(LOG_INFO, "%i GPU devices max detected", *ndevs); exit(*ndevs); } #endif - struct cgpu_info gpus[MAX_GPUDEVICES]; /* Maximum number apparently possible */ struct cgpu_info *cpus; - - #ifdef HAVE_OPENCL /* In dynamic mode, only the first thread of each device will be in use. @@ -624,8 +616,9 @@ void pause_dynamic_threads(int gpu) int i; for (i = 1; i < cgpu->threads; i++) { - struct thr_info *thr = &thr_info[i]; + struct thr_info *thr; + thr = get_thread(i); if (!thr->pause && cgpu->dynamic) { applog(LOG_WARNING, "Disabling extra threads due to dynamic mode."); applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval"); @@ -637,9 +630,6 @@ void pause_dynamic_threads(int gpu) } } - -struct device_api opencl_api; - #endif /* HAVE_OPENCL */ #if defined(HAVE_OPENCL) && defined(HAVE_CURSES) @@ -716,7 +706,7 @@ retry: else wlog("%d\n", gpus[gpu].intensity); for (i = 0; i < mining_threads; i++) { - thr = &thr_info[i]; + thr = get_thread(i); if (thr->cgpu != cgpu) continue; get_datestamp(checkin, &thr->last); @@ -771,9 +761,9 @@ retry: } gpus[selected].deven = DEV_ENABLED; for (i = 0; i < mining_threads; ++i) { - thr = &thr_info[i]; + thr = get_thread(i); cgpu = thr->cgpu; - if (cgpu->api != &opencl_api) + if (cgpu->drv->drv_id != DRIVER_OPENCL) continue; if (dev_from_id(i) != selected) continue; @@ -1158,14 +1148,14 @@ select_cgpu: gpu = cgpu->device_id; for (thr_id = 0; thr_id < mining_threads; ++thr_id) { - thr = &thr_info[thr_id]; + thr = get_thread(thr_id); cgpu = thr->cgpu; - if (cgpu->api != &opencl_api) + if (cgpu->drv->drv_id != DRIVER_OPENCL) continue; if (dev_from_id(thr_id) != gpu) continue; - thr = &thr_info[thr_id]; + thr = get_thread(thr_id); if (!thr) { applog(LOG_WARNING, "No reference to thread %d exists", thr_id); continue; @@ -1183,9 +1173,9 @@ select_cgpu: for (thr_id = 0; thr_id < mining_threads; ++thr_id) { int virtual_gpu; - thr = &thr_info[thr_id]; + thr = get_thread(thr_id); cgpu = thr->cgpu; - if (cgpu->api != &opencl_api) + if (cgpu->drv->drv_id != DRIVER_OPENCL) continue; if (dev_from_id(thr_id) != gpu) continue; @@ -1220,9 +1210,9 @@ select_cgpu: get_datestamp(cgpu->init, &now); for (thr_id = 0; thr_id < mining_threads; ++thr_id) { - thr = &thr_info[thr_id]; + thr = get_thread(thr_id); cgpu = thr->cgpu; - if (cgpu->api != &opencl_api) + if (cgpu->drv->drv_id != DRIVER_OPENCL) continue; if (dev_from_id(thr_id) != gpu) continue; @@ -1243,8 +1233,6 @@ void *reinit_gpu(__maybe_unused void *userdata) #ifdef HAVE_OPENCL -struct device_api opencl_api; - static void opencl_detect() { int i; @@ -1263,7 +1251,7 @@ static void opencl_detect() cgpu = &gpus[i]; cgpu->deven = DEV_ENABLED; - cgpu->api = &opencl_api; + cgpu->drv = &opencl_drv; cgpu->device_id = i; cgpu->threads = opt_g_threads; cgpu->virtual_gpu = i; @@ -1276,7 +1264,7 @@ static void opencl_detect() static void reinit_opencl_device(struct cgpu_info *gpu) { - tq_push(thr_info[gpur_thr_id].q, gpu); + tq_push(control_thr[gpur_thr_id].q, gpu); } #ifdef HAVE_ADL @@ -1299,7 +1287,8 @@ static void get_opencl_statline_before(char *buf, struct cgpu_info *gpu) else tailsprintf(buf, " "); tailsprintf(buf, "| "); - } + } else + gpu->drv->get_statline_before = &blank_get_statline_before; } #endif @@ -1570,10 +1559,11 @@ static void opencl_thread_shutdown(struct thr_info *thr) clReleaseContext(clState->context); } -struct device_api opencl_api = { +struct device_drv opencl_drv = { + .drv_id = DRIVER_OPENCL, .dname = "opencl", .name = "GPU", - .api_detect = opencl_detect, + .drv_detect = opencl_detect, .reinit_device = reinit_opencl_device, #ifdef HAVE_ADL .get_statline_before = get_opencl_statline_before, diff --git a/driver-opencl.h b/driver-opencl.h index c1d61822..22bd9ec2 100644 --- a/driver-opencl.h +++ b/driver-opencl.h @@ -30,6 +30,6 @@ extern void pause_dynamic_threads(int gpu); extern bool have_opencl; extern int opt_platform_id; -extern struct device_api opencl_api; +extern struct device_drv opencl_drv; #endif /* __DEVICE_GPU_H__ */ diff --git a/driver-ztex.c b/driver-ztex.c index 23aa97e4..27b8c26b 100644 --- a/driver-ztex.c +++ b/driver-ztex.c @@ -30,7 +30,7 @@ #define GOLDEN_BACKLOG 5 -struct device_api ztex_api; +struct device_drv ztex_drv; // Forward declarations static void ztex_disable(struct thr_info* thr); @@ -68,7 +68,7 @@ static void ztex_detect(void) for (i = 0; i < cnt; i++) { ztex = calloc(1, sizeof(struct cgpu_info)); - ztex->api = &ztex_api; + ztex->drv = &ztex_drv; ztex->device_ztex = ztex_devices[i]->dev; ztex->threads = 1; ztex->device_ztex->fpgaNum = 0; @@ -82,7 +82,7 @@ static void ztex_detect(void) for (j = 1; j < fpgacount; j++) { ztex = calloc(1, sizeof(struct cgpu_info)); - ztex->api = &ztex_api; + ztex->drv = &ztex_drv; ztex_slave = calloc(1, sizeof(struct libztex_device)); memcpy(ztex_slave, ztex_devices[i]->dev, sizeof(struct libztex_device)); ztex->device_ztex = ztex_slave; @@ -389,15 +389,18 @@ static void ztex_shutdown(struct thr_info *thr) static void ztex_disable(struct thr_info *thr) { + struct cgpu_info *cgpu; + applog(LOG_ERR, "%s: Disabling!", thr->cgpu->device_ztex->repr); - devices[thr->cgpu->device_id]->deven = DEV_DISABLED; + cgpu = get_devices(thr->cgpu->device_id); + cgpu->deven = DEV_DISABLED; ztex_shutdown(thr); } -struct device_api ztex_api = { +struct device_drv ztex_drv = { .dname = "ztex", .name = "ZTX", - .api_detect = ztex_detect, + .drv_detect = ztex_detect, .get_statline_before = ztex_statline_before, .thread_prepare = ztex_prepare, .scanhash = ztex_scanhash, diff --git a/findnonce.c b/findnonce.c index bf39a122..fcf75f23 100644 --- a/findnonce.c +++ b/findnonce.c @@ -204,7 +204,7 @@ static void *postcalc_hash(void *userdata) * end of the res[] array */ if (unlikely(pcd->res[FOUND] & ~FOUND)) { applog(LOG_WARNING, "%s%d: invalid nonce count - HW error", - thr->cgpu->api->name, thr->cgpu->device_id); + thr->cgpu->drv->name, thr->cgpu->device_id); hw_errors++; thr->cgpu->hw_errors++; pcd->res[FOUND] &= FOUND; diff --git a/fpgautils.c b/fpgautils.c index 487395cc..307bec49 100644 --- a/fpgautils.c +++ b/fpgautils.c @@ -104,14 +104,14 @@ int serial_autodetect_devserial(__maybe_unused detectone_func_t detectone, __may #endif } -int _serial_detect(struct device_api *api, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto) +int _serial_detect(struct device_drv *drv, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto) { struct string_elist *iter, *tmp; const char *dev, *colon; bool inhibitauto = false; char found = 0; - size_t namel = strlen(api->name); - size_t dnamel = strlen(api->dname); + size_t namel = strlen(drv->name); + size_t dnamel = strlen(drv->dname); list_for_each_entry_safe(iter, tmp, &scan_devices, list) { dev = iter->string; @@ -119,8 +119,8 @@ int _serial_detect(struct device_api *api, detectone_func_t detectone, autoscan_ size_t idlen = colon - dev; // allow either name:device or dname:device - if ((idlen != namel || strncasecmp(dev, api->name, idlen)) - && (idlen != dnamel || strncasecmp(dev, api->dname, idlen))) + if ((idlen != namel || strncasecmp(dev, drv->name, idlen)) + && (idlen != dnamel || strncasecmp(dev, drv->dname, idlen))) continue; dev = colon + 1; diff --git a/fpgautils.h b/fpgautils.h index 18025488..b979b6c6 100644 --- a/fpgautils.h +++ b/fpgautils.h @@ -16,13 +16,13 @@ typedef bool(*detectone_func_t)(const char*); typedef int(*autoscan_func_t)(); -extern int _serial_detect(struct device_api *api, detectone_func_t, autoscan_func_t, bool force_autoscan); -#define serial_detect_fauto(api, detectone, autoscan) \ - _serial_detect(api, detectone, autoscan, true) -#define serial_detect_auto(api, detectone, autoscan) \ - _serial_detect(api, detectone, autoscan, false) -#define serial_detect(api, detectone) \ - _serial_detect(api, detectone, NULL, false) +extern int _serial_detect(struct device_drv *drv, detectone_func_t, autoscan_func_t, bool force_autoscan); +#define serial_detect_fauto(drv, detectone, autoscan) \ + _serial_detect(drv, detectone, autoscan, true) +#define serial_detect_auto(drv, detectone, autoscan) \ + _serial_detect(drv, detectone, autoscan, false) +#define serial_detect(drv, detectone) \ + _serial_detect(drv, detectone, NULL, false) extern int serial_autodetect_devserial(detectone_func_t, const char *prodname); extern int serial_autodetect_udev(detectone_func_t, const char *prodname); diff --git a/miner.h b/miner.h index ca3df47d..5be502f8 100644 --- a/miner.h +++ b/miner.h @@ -114,7 +114,7 @@ static inline int fsync (int fd) #include "libztex.h" #endif -#ifdef USE_MODMINER +#if defined(USE_MODMINER) || defined(USE_BITFORCE) #include "usbutils.h" #endif @@ -196,6 +196,15 @@ static inline int fsync (int fd) #endif #endif +enum drv_driver { + DRIVER_OPENCL, + DRIVER_ICARUS, + DRIVER_BITFORCE, + DRIVER_MODMINER, + DRIVER_ZTEX, + DRIVER_CPU, +}; + enum alive { LIFE_WELL, LIFE_SICK, @@ -259,16 +268,20 @@ struct gpu_adl { }; #endif +extern void blank_get_statline_before(char *buf, struct cgpu_info __maybe_unused *cgpu); + struct api_data; struct thr_info; struct work; -struct device_api { +struct device_drv { + enum drv_driver drv_id; + char *dname; char *name; - // API-global functions - void (*api_detect)(); + // DRV-global functions + void (*drv_detect)(); // Device-specific functions void (*reinit_device)(struct cgpu_info *); @@ -284,12 +297,31 @@ struct device_api { uint64_t (*can_limit_work)(struct thr_info *); bool (*thread_init)(struct thr_info *); bool (*prepare_work)(struct thr_info *, struct work *); + + /* Which hash work loop this driver uses. */ + void (*hash_work)(struct thr_info *); + /* Two variants depending on whether the device divides work up into + * small pieces or works with whole work items and may or may not have + * a queue of its own. */ int64_t (*scanhash)(struct thr_info *, struct work *, int64_t); + int64_t (*scanwork)(struct thr_info *); + + /* Used to extract work from the hash table of queued work and tell + * the main loop that it should not add any further work to the table. + */ + bool (*queue_full)(struct cgpu_info *); + void (*flush_work)(struct cgpu_info *); + void (*hw_error)(struct thr_info *); void (*thread_shutdown)(struct thr_info *); void (*thread_enable)(struct thr_info *); + + // Does it need to be free()d? + bool copy; }; +extern struct device_drv *copy_drv(struct device_drv*); + enum dev_enable { DEV_ENABLED, DEV_DISABLED, @@ -359,13 +391,15 @@ struct cgminer_pool_stats { uint32_t max_diff_count; uint64_t times_sent; uint64_t bytes_sent; + uint64_t net_bytes_sent; uint64_t times_received; uint64_t bytes_received; + uint64_t net_bytes_received; }; struct cgpu_info { int cgminer_id; - struct device_api *api; + struct device_drv *drv; int device_id; char *name; char *device_path; @@ -374,13 +408,17 @@ struct cgpu_info { #ifdef USE_ZTEX struct libztex_device *device_ztex; #endif -#ifdef USE_MODMINER +#if defined(USE_MODMINER) || defined(USE_BITFORCE) struct cg_usb_device *usbdev; #endif +#ifdef USE_ICARUS int device_fd; +#endif }; +#if defined(USE_MODMINER) || defined(USE_BITFORCE) + struct cg_usb_info usbinfo; +#endif #ifdef USE_MODMINER - int usbstat; char fpgaid; unsigned char clock; pthread_mutex_t *modminer_mutex; @@ -475,6 +513,9 @@ struct cgpu_info { int dev_throttle_count; struct cgminer_stats cgminer_stats; + + pthread_rwlock_t qlock; + struct work *queued_work; }; extern bool add_cgpu(struct cgpu_info*); @@ -715,6 +756,8 @@ extern pthread_mutex_t cgusb_lock; extern pthread_mutex_t hash_lock; extern pthread_mutex_t console_lock; extern pthread_mutex_t ch_lock; +extern pthread_mutex_t mining_thr_lock; +extern pthread_mutex_t devices_lock; extern pthread_mutex_t restart_lock; extern pthread_cond_t restart_cond; @@ -740,6 +783,7 @@ extern void api(int thr_id); extern struct pool *current_pool(void); extern int enabled_pools; extern bool detect_stratum(struct pool *pool, char *url); +extern void print_summary(void); extern struct pool *add_pool(void); extern void add_pool_details(struct pool *pool, bool live, char *url, char *user, char *pass); @@ -755,6 +799,7 @@ extern void add_pool_details(struct pool *pool, bool live, char *url, char *user #define _MAX_INTENSITY_STR "14" #endif +extern bool hotplug_mode; extern struct list_head scan_devices; extern int nDevs; extern int opt_n_threads; @@ -762,7 +807,8 @@ extern int num_processors; extern int hw_errors; extern bool use_syslog; extern bool opt_quiet; -extern struct thr_info *thr_info; +extern struct thr_info *control_thr; +extern struct thr_info **mining_thr; extern struct cgpu_info gpus[MAX_GPUDEVICES]; extern int gpu_threads; #ifdef USE_SCRYPT @@ -927,6 +973,7 @@ struct pool { time_t last_work_time; time_t last_share_time; double last_share_diff; + uint64_t best_diff; struct cgminer_stats cgminer_stats; struct cgminer_pool_stats cgminer_pool_stats; @@ -1058,6 +1105,9 @@ struct modminer_fpga_state { extern void get_datestamp(char *, struct timeval *); extern void submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce); +extern struct work *get_queued(struct cgpu_info *cgpu); +extern void work_completed(struct cgpu_info *cgpu, struct work *work); +extern void hash_queued_work(struct thr_info *mythr); extern void tailsprintf(char *f, const char *fmt, ...); extern void wlogprint(const char *f, ...); extern int curses_int(const char *query); @@ -1066,6 +1116,8 @@ 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 zero_bestshare(void); +extern void zero_stats(void); extern void default_save_file(char *filename); extern bool log_curses_only(int prio, const char *f, va_list ap); extern void clear_logwin(void); @@ -1083,6 +1135,8 @@ extern void clean_work(struct work *work); extern void free_work(struct work *work); extern void __copy_work(struct work *work, struct work *base_work); extern struct work *copy_work(struct work *base_work); +extern struct thr_info *get_thread(int thr_id); +extern struct cgpu_info *get_devices(int id); enum api_data_type { API_ESCAPE, diff --git a/miner.php b/miner.php index 8c393ac8..dd9ff399 100644 --- a/miner.php +++ b/miner.php @@ -8,7 +8,7 @@ global $checklastshare, $poolinputs, $hidefields; global $ignorerefresh, $changerefresh, $autorefresh; global $allowcustompages, $customsummarypages; global $miner_font_family, $miner_font_size; -global $colouroverride, $placebuttons; +global $colouroverride, $placebuttons, $userlist; # # See API-README for more details of these variables and how # to configure miner.php @@ -20,6 +20,9 @@ $title = 'Mine'; # Set $readonly to false then it will check cgminer 'privileged' $readonly = false; # +# Set $userlist to null to allow anyone access or read API-README +$userlist = null; +# # Set $notify to false to NOT attempt to display the notify command # Set $notify to true to attempt to display the notify command $notify = true; @@ -113,16 +116,17 @@ $poolspage = array( 'POOL.Difficulty Rejected=Diff Rej', 'POOL.Has Stratum=Stratum', 'POOL.Stratum Active=StrAct', 'POOL.Has GBT=GBT', 'STATS.Times Sent=TSent', - 'STATS.Bytes Sent=BSent', 'STATS.Times Recv=TRecv', - 'STATS.Bytes Recv=BRecv')); + 'STATS.Bytes Sent=BSent', 'STATS.Net Bytes Sent=NSent', + 'STATS.Times Recv=TRecv', 'STATS.Bytes Recv=BRecv', + 'STATS.Net Bytes Recv=NRecv')); # $poolssum = array( 'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted', 'Rejected', 'Utility', 'Hardware Errors', 'Work Utility'), 'POOL+STATS' => array('POOL.Difficulty Accepted', 'POOL.Difficulty Rejected', - 'STATS.Times Sent', 'STATS.Bytes Sent', - 'STATS.Times Recv', 'STATS.Bytes Recv')); + 'STATS.Times Sent', 'STATS.Bytes Sent', 'STATS.Net Bytes Sent', + 'STATS.Times Recv', 'STATS.Bytes Recv', 'STATS.Net Bytes Recv')); # $poolsext = array( 'POOL+STATS' => array( @@ -130,7 +134,8 @@ $poolsext = array( 'group' => array('POOL.URL', 'POOL.Has Stratum', 'POOL.Stratum Active', 'POOL.Has GBT'), 'calc' => array('POOL.Difficulty Accepted' => 'sum', 'POOL.Difficulty Rejected' => 'sum', 'STATS.Times Sent' => 'sum', 'STATS.Bytes Sent' => 'sum', - 'STATS.Times Recv' => 'sum', 'STATS.Bytes Recv' => 'sum'), + 'STATS.Net Bytes Sent' => 'sum', 'STATS.Times Recv' => 'sum', + 'STATS.Bytes Recv' => 'sum', 'STATS.Net Bytes Recv' => 'sum'), 'having' => array(array('STATS.Bytes Recv', '>', 0))) ); @@ -212,6 +217,10 @@ $rigerror = array(); global $rownum; $rownum = 0; # +// Login +global $ses; +$ses = 'rutroh'; +# function getcss($cssname, $dom = false) { global $colourtable, $colouroverride; @@ -239,7 +248,7 @@ function getdom($domname) return getcss($domname, true); } # -function htmlhead($checkapi, $rig, $pg = null) +function htmlhead($checkapi, $rig, $pg = null, $noscript = false) { global $title, $miner_font_family, $miner_font_size; global $error, $readonly, $poolinputs, $here; @@ -285,8 +294,10 @@ td.lst { $miner_font ".getcss('td.lst')."} td.hi { $miner_font ".getcss('td.hi')."} td.lo { $miner_font ".getcss('td.lo')."} - -\n"; +} ?> -
@@ -578,6 +590,14 @@ function classlastshare($when, $alldata, $warnclass, $errorclass) return ''; } # +function endzero($num) +{ + $rep = preg_replace('/0*$/', '', $num); + if ($rep === '') + $rep = '0'; + return $rep; +} +# function fmt($section, $name, $value, $when, $alldata) { global $dfmt, $rownum; @@ -840,12 +860,16 @@ function fmt($section, $name, $value, $when, $alldata) case 'total.Diff1 Work': case 'STATS.Times Sent': case 'STATS.Bytes Sent': + case 'STATS.Net Bytes Sent': case 'STATS.Times Recv': case 'STATS.Bytes Recv': + case 'STATS.Net Bytes Recv': case 'total.Times Sent': case 'total.Bytes Sent': + case 'total.Net Bytes Sent': case 'total.Times Recv': case 'total.Bytes Recv': + case 'total.Net Bytes Recv': $parts = explode('.', $value, 2); if (count($parts) == 1) $dec = ''; @@ -853,6 +877,23 @@ function fmt($section, $name, $value, $when, $alldata) $dec = '.'.$parts[1]; $ret = number_format((float)$parts[0]).$dec; break; + case 'STATS.Hs': + case 'STATS.W': + case 'STATS.history_time': + case 'STATS.Pool Wait': + case 'STATS.Pool Max': + case 'STATS.Pool Min': + case 'STATS.Pool Av': + case 'STATS.Min Diff': + case 'STATS.Max Diff': + case 'STATS.Work Diff': + $parts = explode('.', $value, 2); + if (count($parts) == 1) + $dec = ''; + else + $dec = '.'.endzero($parts[1]); + $ret = number_format((float)$parts[0]).$dec; + break; case 'GPU.Status': case 'PGA.Status': case 'DEVS.Status': @@ -1507,7 +1548,6 @@ function doforeach($cmd, $des, $sum, $head, $datetime) # function refreshbuttons() { - global $readonly; global $ignorerefresh, $changerefresh, $autorefresh; if ($ignorerefresh == false && $changerefresh == true) @@ -1521,7 +1561,7 @@ function refreshbuttons() # function pagebuttons($rig, $pg) { - global $readonly, $rigs; + global $readonly, $rigs, $userlist, $ses; global $allowcustompages, $customsummarypages; if ($rig === null) @@ -1557,18 +1597,33 @@ function pagebuttons($rig, $pg) } echo '"; } # @@ -2015,6 +2076,8 @@ function docalc($func, $data) if (strcasecmp($val, $ans) > 0) $ans = $val; return $ans; + case 'count': + return count($data); case 'any': default: return $data[0]; @@ -2410,13 +2473,126 @@ function showcustompage($pagename) pagebuttons(null, $pagename); } # +function onlylogin() +{ + global $here; + + htmlhead(false, null, null, true); + +?> + + +No rigs defined"); + return; + } if ($ignorerefresh == false) { @@ -2425,52 +2601,65 @@ function display() $autorefresh = intval($ref); } - $rig = trim(getparam('rig', true)); - - $arg = trim(getparam('arg', true)); - $preprocess = null; - if ($arg != null and $arg != '') + if ($pagesonly !== true) { - $num = null; - if ($rig != null and $rig != '') - { - if ($rig >= 0 and $rig < count($rigs)) - $num = $rig; - } - else - if (count($rigs) == 0) - $num = 0; + $rig = trim(getparam('rig', true)); - if ($num != null) + $arg = trim(getparam('arg', true)); + $preprocess = null; + if ($arg != null and $arg != '') { - $parts = explode(':', $rigs[$num], 3); - if (count($parts) >= 2) + if ($rig != null and $rig != '' and $rig >= 0 and $rig < count($rigs)) { - $miner = $parts[0]; - $port = $parts[1]; + $parts = explode(':', $rigs[$rig], 3); + if (count($parts) >= 2) + { + $miner = $parts[0]; + $port = $parts[1]; - if ($readonly !== true) - $preprocess = $arg; + if ($readonly !== true) + $preprocess = $arg; + } } } } - if ($rigs == null or count($rigs) == 0) - { - otherrow(""); - return; - } - if ($allowcustompages === true) { $pg = trim(getparam('pg', true)); - if ($pg != null && $pg != '') + if ($pagesonly === true) + { + if ($pg !== null && $pg !== '') + { + if ($userlist !== null && isset($userlist['def']) + && !in_array($pg, $userlist['def'])) + $pg = null; + } + else + { + if ($userlist !== null && isset($userlist['def'])) + foreach ($userlist['def'] as $pglook) + if (isset($customsummarypages[$pglook])) + { + $pg = $pglook; + break; + } + } + } + + if ($pg !== null && $pg !== '') { showcustompage($pg); return; } } + if ($pagesonly === true) + { + onlylogin(); + return; + } + if (count($rigs) == 1) { $parts = explode(':', $rigs[0], 3); diff --git a/usbutils.c b/usbutils.c index ad3ff270..f0dd7c77 100644 --- a/usbutils.c +++ b/usbutils.c @@ -1,5 +1,5 @@ /* - * Copyright 2012 Andrew Smith + * Copyright 2012-2013 Andrew Smith * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -16,6 +16,10 @@ #include "miner.h" #include "usbutils.h" +#define NODEV(err) ((err) == LIBUSB_ERROR_NO_DEVICE || \ + (err) == LIBUSB_ERROR_PIPE || \ + (err) == LIBUSB_ERROR_OTHER) + #ifdef USE_ICARUS #define DRV_ICARUS 1 #endif @@ -36,11 +40,21 @@ #define EPO(x) (LIBUSB_ENDPOINT_OUT | (unsigned char)(x)) #ifdef WIN32 +#define BITFORCE_TIMEOUT_MS 500 #define MODMINER_TIMEOUT_MS 200 #else +#define BITFORCE_TIMEOUT_MS 200 #define MODMINER_TIMEOUT_MS 100 #endif +#ifdef USE_BITFORCE +// N.B. transfer size is 512 with USB2.0, but only 64 with USB1.1 +static struct usb_endpoints bfl_eps[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } +}; +#endif + #ifdef USE_MODMINER static struct usb_endpoints mmq_eps[] = { { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0 }, @@ -48,7 +62,7 @@ static struct usb_endpoints mmq_eps[] = { }; #endif -// TODO: Add support for (at least) Interrupt endpoints +// TODO: Add support for (at least) Isochronous endpoints static struct usb_find_devices find_dev[] = { /* #ifdef USE_ICARUS @@ -56,55 +70,52 @@ static struct usb_find_devices find_dev[] = { { DRV_ICARUS, "LOT", 0x0403, 0x6001, false, EPI(0), EPO(0), 1 }, { DRV_ICARUS, "CM1", 0x067b, 0x0230, false, EPI(0), EPO(0), 1 }, #endif +*/ #ifdef USE_BITFORCE - { DRV_BITFORCE, "BFL", 0x0403, 0x6014, true, EPI(1), EPO(2), 1 }, + { + .drv = DRV_BITFORCE, + .name = "BFL", + .idVendor = 0x0403, + .idProduct = 0x6014, + .kernel = 0, + .config = 1, + .interface = 0, + .timeout = BITFORCE_TIMEOUT_MS, + .epcount = ARRAY_SIZE(bfl_eps), + .eps = bfl_eps }, #endif -*/ #ifdef USE_MODMINER { .drv = DRV_MODMINER, .name = "MMQ", .idVendor = 0x1fc9, .idProduct = 0x0003, + .kernel = 0, .config = 1, .interface = 1, .timeout = MODMINER_TIMEOUT_MS, .epcount = ARRAY_SIZE(mmq_eps), .eps = mmq_eps }, #endif - { DRV_LAST, NULL, 0, 0, 0, 0, 0, 0, NULL } + { DRV_LAST, NULL, 0, 0, 0, 0, 0, 0, 0, NULL } }; #ifdef USE_BITFORCE -extern struct device_api bitforce_api; +extern struct device_drv bitforce_drv; #endif #ifdef USE_ICARUS -extern struct device_api icarus_api; +extern struct device_drv icarus_drv; #endif #ifdef USE_MODMINER -extern struct device_api modminer_api; +extern struct device_drv modminer_drv; #endif -/* - * Our own internal list of used USB devices - * So two drivers or a single driver searching - * can't touch the same device during detection - */ -struct usb_list { - uint8_t bus_number; - uint8_t device_address; - uint8_t filler[2]; - struct usb_list *prev; - struct usb_list *next; -}; - #define STRBUFLEN 256 static const char *BLANK = ""; -static pthread_mutex_t *list_lock = NULL; -static struct usb_list *usb_head = NULL; +static bool stats_initialised = false; struct cg_usb_stats_item { uint64_t count; @@ -138,6 +149,7 @@ static int next_stat = 0; static const char **usb_commands; +static const char *C_REJECTED_S = "RejectedNoDevice"; static const char *C_PING_S = "Ping"; static const char *C_CLEAR_S = "Clear"; static const char *C_REQUESTVERSION_S = "RequestVersion"; @@ -160,6 +172,18 @@ static const char *C_SENDWORK_S = "SendWork"; static const char *C_SENDWORKSTATUS_S = "SendWorkStatus"; static const char *C_REQUESTWORKSTATUS_S = "RequestWorkStatus"; static const char *C_GETWORKSTATUS_S = "GetWorkStatus"; +static const char *C_REQUESTIDENTIFY_S = "RequestIdentify"; +static const char *C_GETIDENTIFY_S = "GetIdentify"; +static const char *C_REQUESTFLASH_S = "RequestFlash"; +static const char *C_REQUESTSENDWORK_S = "RequestSendWork"; +static const char *C_REQUESTSENDWORKSTATUS_S = "RequestSendWorkStatus"; +static const char *C_RESET_S = "Reset"; +static const char *C_SETBAUD_S = "SetBaud"; +static const char *C_SETDATA_S = "SetDataCtrl"; +static const char *C_SETFLOW_S = "SetFlowCtrl"; +static const char *C_SETMODEM_S = "SetModemCtrl"; +static const char *C_PURGERX_S = "PurgeRx"; +static const char *C_PURGETX_S = "PurgeTx"; #ifdef EOL #undef EOL @@ -501,10 +525,8 @@ static void cgusb_check_init() { mutex_lock(&cgusb_lock); - if (list_lock == NULL) { - list_lock = calloc(1, sizeof(*list_lock)); - mutex_init(list_lock); - + if (stats_initialised == false) { + // N.B. environment LIBUSB_DEBUG also sets libusb_set_debug() if (opt_usbdump >= 0) { libusb_set_debug(NULL, opt_usbdump); usb_all(); @@ -515,6 +537,7 @@ static void cgusb_check_init() // use constants so the stat generation is very quick // and the association between number and name can't // be missalined easily + usb_commands[C_REJECTED] = C_REJECTED_S; usb_commands[C_PING] = C_PING_S; usb_commands[C_CLEAR] = C_CLEAR_S; usb_commands[C_REQUESTVERSION] = C_REQUESTVERSION_S; @@ -537,146 +560,265 @@ static void cgusb_check_init() usb_commands[C_SENDWORKSTATUS] = C_SENDWORKSTATUS_S; usb_commands[C_REQUESTWORKSTATUS] = C_REQUESTWORKSTATUS_S; usb_commands[C_GETWORKSTATUS] = C_GETWORKSTATUS_S; + usb_commands[C_REQUESTIDENTIFY] = C_REQUESTIDENTIFY_S; + usb_commands[C_GETIDENTIFY] = C_GETIDENTIFY_S; + usb_commands[C_REQUESTFLASH] = C_REQUESTFLASH_S; + usb_commands[C_REQUESTSENDWORK] = C_REQUESTSENDWORK_S; + usb_commands[C_REQUESTSENDWORKSTATUS] = C_REQUESTSENDWORKSTATUS_S; + usb_commands[C_RESET] = C_RESET_S; + usb_commands[C_SETBAUD] = C_SETBAUD_S; + usb_commands[C_SETDATA] = C_SETDATA_S; + usb_commands[C_SETFLOW] = C_SETFLOW_S; + usb_commands[C_SETMODEM] = C_SETMODEM_S; + usb_commands[C_PURGERX] = C_PURGERX_S; + usb_commands[C_PURGETX] = C_PURGETX_S; + + stats_initialised = true; } mutex_unlock(&cgusb_lock); } -static bool in_use(libusb_device *dev, bool lock) -{ - struct usb_list *usb_tmp; - bool used = false; - uint8_t bus_number; - uint8_t device_address; - - bus_number = libusb_get_bus_number(dev); - device_address = libusb_get_device_address(dev); - - if (lock) - mutex_lock(list_lock); - - if ((usb_tmp = usb_head)) - do { - if (bus_number == usb_tmp->bus_number - && device_address == usb_tmp->device_address) { - used = true; - break; - } - - usb_tmp = usb_tmp->next; - - } while (usb_tmp != usb_head); +#ifndef WIN32 +#include +#include +#include +#include +#include +#include +#include +#include + +union semun { + int sem; + struct semid_ds *seminfo; + ushort *all; +}; +#endif - if (lock) - mutex_unlock(list_lock); +// Any errors should always be printed since they will rarely if ever occur +// and thus it is best to always display them +static bool cgminer_usb_lock_bd(struct device_drv *drv, uint8_t bus_number, uint8_t device_address) +{ +#ifdef WIN32 + struct cgpu_info *cgpu; + HANDLE usbMutex; + char name[64]; + DWORD res; + int i; - return used; -} + sprintf(name, "cgminer-usb-%d-%d", (int)bus_number, (int)device_address); -static void add_used(libusb_device *dev, bool lock) -{ - struct usb_list *usb_tmp; - char buf[128]; - uint8_t bus_number; - uint8_t device_address; + usbMutex = CreateMutex(NULL, FALSE, name); + if (usbMutex == NULL) { + applog(LOG_ERR, + "MTX: %s USB failed to get '%s' err (%d)", + drv->dname, name, GetLastError()); + return false; + } - bus_number = libusb_get_bus_number(dev); - device_address = libusb_get_device_address(dev); + res = WaitForSingleObject(usbMutex, 0); + switch(res) { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + // Am I using it already? + for (i = 0; i < total_devices; i++) { + cgpu = get_devices(i); + if (cgpu->usbinfo.bus_number == bus_number && + cgpu->usbinfo.device_address == device_address && + cgpu->usbinfo.nodev == false) { + if (ReleaseMutex(usbMutex)) { + applog(LOG_WARNING, + "MTX: %s USB can't get '%s' - device in use", + drv->dname, name); + goto fail; + } + applog(LOG_ERR, + "MTX: %s USB can't get '%s' - device in use - failure (%d)", + drv->dname, name, GetLastError()); + goto fail; + } + } + return true; + case WAIT_TIMEOUT: + if (!hotplug_mode) + applog(LOG_WARNING, + "MTX: %s USB failed to get '%s' - device in use", + drv->dname, name); + goto fail; + case WAIT_FAILED: + applog(LOG_ERR, + "MTX: %s USB failed to get '%s' err (%d)", + drv->dname, name, GetLastError()); + goto fail; + default: + applog(LOG_ERR, + "MTX: %s USB failed to get '%s' unknown reply (%d)", + drv->dname, name, res); + goto fail; + } - if (lock) - mutex_lock(list_lock); + CloseHandle(usbMutex); + return true; +fail: + CloseHandle(usbMutex); + return false; +#else + struct semid_ds seminfo; + union semun opt; + char name[64]; + key_t key; + int fd, sem, count; + + sprintf(name, "/tmp/cgminer-usb-%d-%d", (int)bus_number, (int)device_address); + fd = open(name, O_CREAT|O_RDONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (fd == -1) { + applog(LOG_ERR, + "SEM: %s USB open failed '%s' err (%d) %s", + drv->dname, name, errno, strerror(errno)); + return false; + } + close(fd); + key = ftok(name, 'K'); + sem = semget(key, 1, IPC_CREAT | IPC_EXCL | 438); + if (sem < 0) { + if (errno != EEXIST) { + applog(LOG_ERR, + "SEM: %s USB failed to get '%s' err (%d) %s", + drv->dname, name, errno, strerror(errno)); + return false; + } - if (in_use(dev, false)) { - if (lock) - mutex_unlock(list_lock); + sem = semget(key, 1, 0); + if (sem < 0) { + applog(LOG_ERR, + "SEM: %s USB failed to access '%s' err (%d) %s", + drv->dname, name, errno, strerror(errno)); + return false; + } - sprintf(buf, "add_used() duplicate bus_number %d device_address %d", - bus_number, device_address); - quit(1, buf); + opt.seminfo = &seminfo; + count = 0; + while (++count) { + // Should NEVER take 100ms + if (count > 99) { + applog(LOG_ERR, + "SEM: %s USB timeout waiting for (%d) '%s'", + drv->dname, sem, name); + return false; + } + if (semctl(sem, 0, IPC_STAT, opt) == -1) { + applog(LOG_ERR, + "SEM: %s USB failed to wait for (%d) '%s' count %d err (%d) %s", + drv->dname, sem, name, count, errno, strerror(errno)); + return false; + } + if (opt.seminfo->sem_otime != 0) + break; + nmsleep(1); + } } - usb_tmp = malloc(sizeof(*usb_tmp)); - - usb_tmp->bus_number = bus_number; - usb_tmp->device_address = device_address; - - if (usb_head) { - // add to end - usb_tmp->prev = usb_head->prev; - usb_tmp->next = usb_head; - usb_head->prev = usb_tmp; - usb_tmp->prev->next = usb_tmp; - } else { - usb_tmp->prev = usb_tmp; - usb_tmp->next = usb_tmp; - usb_head = usb_tmp; + struct sembuf sops[] = { + { 0, 0, IPC_NOWAIT | SEM_UNDO }, + { 0, 1, IPC_NOWAIT | SEM_UNDO } + }; + + if (semop(sem, sops, 2)) { + if (errno == EAGAIN) { + if (!hotplug_mode) + applog(LOG_WARNING, + "SEM: %s USB failed to get (%d) '%s' - device in use", + drv->dname, sem, name); + } else { + applog(LOG_DEBUG, + "SEM: %s USB failed to get (%d) '%s' err (%d) %s", + drv->dname, sem, name, errno, strerror(errno)); + } + return false; } - if (lock) - mutex_unlock(list_lock); + return true; +#endif } -static void release(uint8_t bus_number, uint8_t device_address, bool lock) +static bool cgminer_usb_lock(struct device_drv *drv, libusb_device *dev) { - struct usb_list *usb_tmp; - bool found = false; - char buf[128]; - - if (lock) - mutex_lock(list_lock); - - usb_tmp = usb_head; - if (usb_tmp) - do { - if (bus_number == usb_tmp->bus_number - && device_address == usb_tmp->device_address) { - found = true; - break; - } - - usb_tmp = usb_tmp->next; + return cgminer_usb_lock_bd(drv, libusb_get_bus_number(dev), libusb_get_device_address(dev)); +} - } while (usb_tmp != usb_head); +// Any errors should always be printed since they will rarely if ever occur +// and thus it is best to always display them +static void cgminer_usb_unlock_bd(struct device_drv *drv, uint8_t bus_number, uint8_t device_address) +{ +#ifdef WIN32 + HANDLE usbMutex; + char name[64]; - if (!found) { - if (lock) - mutex_unlock(list_lock); + sprintf(name, "cgminer-usb-%d-%d", (int)bus_number, (int)device_address); - sprintf(buf, "release() unknown: bus_number %d device_address %d", - bus_number, device_address); - quit(1, buf); + usbMutex = CreateMutex(NULL, FALSE, name); + if (usbMutex == NULL) { + applog(LOG_ERR, + "MTX: %s USB failed to get '%s' for release err (%d)", + drv->dname, name, GetLastError()); + return; } - if (usb_tmp->next == usb_tmp) { - usb_head = NULL; - } else { - usb_tmp->next->prev = usb_tmp->prev; - usb_tmp->prev->next = usb_tmp->next; - } + if (!ReleaseMutex(usbMutex)) + applog(LOG_ERR, + "MTX: %s USB failed to release '%s' err (%d)", + drv->dname, name, GetLastError()); - if (lock) - mutex_unlock(list_lock); + CloseHandle(usbMutex); + return; +#else + char name[64]; + key_t key; + int fd, sem; + + sprintf(name, "/tmp/cgminer-usb-%d-%d", (int)bus_number, (int)device_address); + fd = open(name, O_CREAT|O_RDONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (fd == -1) { + applog(LOG_ERR, + "SEM: %s USB open failed '%s' for release err (%d) %s", + drv->dname, name, errno, strerror(errno)); + return; + } + close(fd); + key = ftok(name, 'K'); + + sem = semget(key, 1, 0); + if (sem < 0) { + applog(LOG_ERR, + "SEM: %s USB failed to get '%s' for release err (%d) %s", + drv->dname, name, errno, strerror(errno)); + return; + } - free(usb_tmp); -} + struct sembuf sops[] = { + { 0, -1, SEM_UNDO } + }; -static void release_dev(libusb_device *dev, bool lock) -{ - uint8_t bus_number; - uint8_t device_address; + // Allow a 10ms timeout + // exceeding this timeout means it would probably never succeed anyway + struct timespec timeout = { 0, 10000000 }; - bus_number = libusb_get_bus_number(dev); - device_address = libusb_get_device_address(dev); + // Wait forever since we shoud be the one who has it + if (semtimedop(sem, sops, 1, &timeout)) { + applog(LOG_ERR, + "SEM: %d USB failed to release '%s' err (%d) %s", + drv->dname, name, errno, strerror(errno)); + } - release(bus_number, device_address, lock); + return; +#endif } -#if 0 -static void release_cgusb(struct cg_usb_device *cgusb, bool lock) +static void cgminer_usb_unlock(struct device_drv *drv, libusb_device *dev) { - release(cgusb->bus_number, cgusb->device_address, lock); + cgminer_usb_unlock_bd(drv, libusb_get_bus_number(dev), libusb_get_device_address(dev)); } -#endif static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb) { @@ -691,6 +833,8 @@ static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb) free(cgusb->descriptor); + free(cgusb->found); + free(cgusb); return NULL; @@ -698,11 +842,43 @@ static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb) void usb_uninit(struct cgpu_info *cgpu) { + // May have happened already during a failed initialisation + // if release_cgpu() was called due to a USB NODEV(err) + if (!cgpu->usbdev) + return; libusb_release_interface(cgpu->usbdev->handle, cgpu->usbdev->found->interface); libusb_close(cgpu->usbdev->handle); cgpu->usbdev = free_cgusb(cgpu->usbdev); } +static void release_cgpu(struct cgpu_info *cgpu) +{ + struct cg_usb_device *cgusb = cgpu->usbdev; + struct cgpu_info *lookcgpu; + int i; + + cgpu->usbinfo.nodev = true; + cgpu->usbinfo.nodev_count++; + gettimeofday(&(cgpu->usbinfo.last_nodev), NULL); + + // Any devices sharing the same USB device should be marked also + // Currently only MMQ shares a USB device + for (i = 0; i < total_devices; i++) { + lookcgpu = get_devices(i); + if (lookcgpu != cgpu && lookcgpu->usbdev == cgusb) { + lookcgpu->usbinfo.nodev = true; + lookcgpu->usbinfo.nodev_count++; + memcpy(&(lookcgpu->usbinfo.last_nodev), + &(cgpu->usbinfo.last_nodev), sizeof(struct timeval)); + lookcgpu->usbdev = NULL; + } + } + + usb_uninit(cgpu); + + cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address); +} + bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found) { struct cg_usb_device *cgusb = NULL; @@ -710,19 +886,25 @@ bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find const struct libusb_interface_descriptor *idesc; const struct libusb_endpoint_descriptor *epdesc; unsigned char strbuf[STRBUFLEN+1]; + char devstr[STRBUFLEN+1]; int err, i, j, k; + cgpu->usbinfo.bus_number = libusb_get_bus_number(dev); + cgpu->usbinfo.device_address = libusb_get_device_address(dev); + + sprintf(devstr, "- %s device %d:%d", found->name, + cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address); + cgusb = calloc(1, sizeof(*cgusb)); cgusb->found = found; - cgusb->bus_number = libusb_get_bus_number(dev); - cgusb->device_address = libusb_get_device_address(dev); - cgusb->descriptor = calloc(1, sizeof(*(cgusb->descriptor))); err = libusb_get_device_descriptor(dev, cgusb->descriptor); if (err) { - applog(LOG_ERR, "USB init failed to get descriptor, err %d", err); + applog(LOG_DEBUG, + "USB init failed to get descriptor, err %d %s", + err, devstr); goto dame; } @@ -730,50 +912,70 @@ bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find if (err) { switch (err) { case LIBUSB_ERROR_ACCESS: - applog(LOG_ERR, "USB init open device failed, err %d, you dont have priviledge to access the device", err); + applog(LOG_ERR, + "USB init open device failed, err %d, " + "you dont have priviledge to access %s", + err, devstr); break; #ifdef WIN32 // Windows specific message case LIBUSB_ERROR_NOT_SUPPORTED: - applog(LOG_ERR, "USB init, open device failed, err %d, you need to install a Windows USB driver for the device", err); + applog(LOG_ERR, + "USB init, open device failed, err %d, " + "you need to install a Windows USB driver for %s", + err, devstr); break; #endif default: - applog(LOG_ERR, "USB init, open device failed, err %d", err); + applog(LOG_DEBUG, + "USB init, open failed, err %d %s", + err, devstr); } goto dame; } - if (libusb_kernel_driver_active(cgusb->handle, 0) == 1) { - applog(LOG_WARNING, "USB init, kernel attached ..."); - if (libusb_detach_kernel_driver(cgusb->handle, 0) == 0) - applog(LOG_WARNING, "USB init, kernel detached successfully"); - else - applog(LOG_WARNING, "USB init, kernel detach failed :("); +#ifndef WIN32 + if (libusb_kernel_driver_active(cgusb->handle, found->kernel) == 1) { + applog(LOG_DEBUG, "USB init, kernel attached ... %s", devstr); + err = libusb_detach_kernel_driver(cgusb->handle, found->kernel); + if (err == 0) { + applog(LOG_DEBUG, + "USB init, kernel detached successfully %s", + devstr); + } else { + applog(LOG_WARNING, + "USB init, kernel detach failed, err %d in use? %s", + err, devstr); + goto cldame; + } } +#endif err = libusb_set_configuration(cgusb->handle, found->config); if (err) { switch(err) { case LIBUSB_ERROR_BUSY: - applog(LOG_WARNING, "USB init, %s device %d:%d in use", - found->name, cgusb->bus_number, cgusb->device_address); + applog(LOG_WARNING, + "USB init, set config %d in use %s", + found->config, devstr); break; default: - applog(LOG_DEBUG, "USB init, failed to set config to %d, err %d", - found->config, err); + applog(LOG_DEBUG, + "USB init, failed to set config to %d, err %d %s", + found->config, err, devstr); } goto cldame; } err = libusb_get_active_config_descriptor(dev, &config); if (err) { - applog(LOG_DEBUG, "USB init, failed to get config descriptor %d, err %d", - found->config, err); + applog(LOG_DEBUG, + "USB init, failed to get config descriptor, err %d %s", + err, devstr); goto cldame; } - if ((int)(config->bNumInterfaces) < found->interface) + if ((int)(config->bNumInterfaces) <= found->interface) goto cldame; for (i = 0; i < found->epcount; i++) @@ -802,8 +1004,17 @@ bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find err = libusb_claim_interface(cgusb->handle, found->interface); if (err) { - applog(LOG_DEBUG, "USB init, claim interface %d failed, err %d", - found->interface, err); + switch(err) { + case LIBUSB_ERROR_BUSY: + applog(LOG_WARNING, + "USB init, claim interface %d in use %s", + found->interface, devstr); + break; + default: + applog(LOG_DEBUG, + "USB init, claim interface %d failed, err %d %s", + found->interface, err, devstr); + } goto cldame; } @@ -837,12 +1048,23 @@ bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find // cgusb->fwVersion <- for temp1/temp2 decision? or serial? (driver-modminer.c) // cgusb->interfaceVersion - applog(LOG_DEBUG, "USB init device bus_number=%d device_address=%d usbver=%04x prod='%s' manuf='%s' serial='%s'", (int)(cgusb->bus_number), (int)(cgusb->device_address), cgusb->usbver, cgusb->prod_string, cgusb->manuf_string, cgusb->serial_string); + applog(LOG_DEBUG, + "USB init %s usbver=%04x prod='%s' manuf='%s' serial='%s'", + devstr, cgusb->usbver, cgusb->prod_string, + cgusb->manuf_string, cgusb->serial_string); cgpu->usbdev = cgusb; libusb_free_config_descriptor(config); + // Allow a name change based on the idVendor+idProduct + // N.B. must be done before calling add_cgpu() + if (strcmp(cgpu->drv->name, found->name)) { + if (!cgpu->drv->copy) + cgpu->drv = copy_drv(cgpu->drv); + cgpu->drv->name = (char *)(found->name); + } + return true; cldame: @@ -859,7 +1081,7 @@ dame: return false; } -static bool usb_check_device(struct device_api *api, struct libusb_device *dev, struct usb_find_devices *look) +static bool usb_check_device(struct device_drv *drv, struct libusb_device *dev, struct usb_find_devices *look) { struct libusb_device_descriptor desc; int err; @@ -871,26 +1093,26 @@ static bool usb_check_device(struct device_api *api, struct libusb_device *dev, } if (desc.idVendor != look->idVendor || desc.idProduct != look->idProduct) { - applog(LOG_DEBUG, "%s looking for %04x:%04x but found %04x:%04x instead", - api->name, look->idVendor, look->idProduct, desc.idVendor, desc.idProduct); + applog(LOG_DEBUG, "%s looking for %s %04x:%04x but found %04x:%04x instead", + drv->name, look->name, look->idVendor, look->idProduct, desc.idVendor, desc.idProduct); return false; } - applog(LOG_DEBUG, "%s looking for and found %04x:%04x", - api->name, look->idVendor, look->idProduct); + applog(LOG_DEBUG, "%s looking for and found %s %04x:%04x", + drv->name, look->name, look->idVendor, look->idProduct); return true; } -static struct usb_find_devices *usb_check_each(int drv, struct device_api *api, struct libusb_device *dev) +static struct usb_find_devices *usb_check_each(int drvnum, struct device_drv *drv, struct libusb_device *dev) { struct usb_find_devices *found; int i; for (i = 0; find_dev[i].drv != DRV_LAST; i++) - if (find_dev[i].drv == drv) { - if (usb_check_device(api, dev, &(find_dev[i]))) { + if (find_dev[i].drv == drvnum) { + if (usb_check_device(drv, dev, &(find_dev[i]))) { found = malloc(sizeof(*found)); memcpy(found, &(find_dev[i]), sizeof(*found)); return found; @@ -900,27 +1122,27 @@ static struct usb_find_devices *usb_check_each(int drv, struct device_api *api, return NULL; } -static struct usb_find_devices *usb_check(__maybe_unused struct device_api *api, __maybe_unused struct libusb_device *dev) +static struct usb_find_devices *usb_check(__maybe_unused struct device_drv *drv, __maybe_unused struct libusb_device *dev) { #ifdef USE_BITFORCE - if (api == &bitforce_api) - return usb_check_each(DRV_BITFORCE, api, dev); + if (drv->drv_id == DRIVER_BITFORCE) + return usb_check_each(DRV_BITFORCE, drv, dev); #endif #ifdef USE_ICARUS - if (api == &icarus_api) - return usb_check_each(DRV_ICARUS, api, dev); + if (drv->drv_id == DRIVER_ICARUS) + return usb_check_each(DRV_ICARUS, drv, dev); #endif #ifdef USE_MODMINER - if (api == &modminer_api) - return usb_check_each(DRV_MODMINER, api, dev); + if (drv->drv_id == DRIVER_MODMINER) + return usb_check_each(DRV_MODMINER, drv, dev); #endif return NULL; } -void usb_detect(struct device_api *api, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *)) +void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *)) { libusb_device **list; ssize_t count, i; @@ -938,21 +1160,14 @@ void usb_detect(struct device_api *api, bool (*device_detect)(struct libusb_devi applog(LOG_DEBUG, "USB scan devices: found no devices"); for (i = 0; i < count; i++) { - mutex_lock(list_lock); - - if (in_use(list[i], false)) - mutex_unlock(list_lock); - else { - add_used(list[i], false); - - mutex_unlock(list_lock); - - found = usb_check(api, list[i]); - if (!found) - release_dev(list[i], true); - else + found = usb_check(drv, list[i]); + if (found != NULL) { + if (cgminer_usb_lock(drv, list[i]) == false) + free(found); + else { if (!device_detect(list[i], found)) - release_dev(list[i], true); + cgminer_usb_unlock(drv, list[i]); + } } } @@ -1055,10 +1270,10 @@ static void newstats(struct cgpu_info *cgpu) { int i; - cgpu->usbstat = ++next_stat; + cgpu->usbinfo.usbstat = ++next_stat; usb_stats = realloc(usb_stats, sizeof(*usb_stats) * next_stat); - usb_stats[next_stat-1].name = cgpu->api->name; + usb_stats[next_stat-1].name = cgpu->drv->name; usb_stats[next_stat-1].device_id = -1; usb_stats[next_stat-1].details = calloc(1, sizeof(struct cg_usb_stats_details) * C_MAX * 2); for (i = 1; i < C_MAX * 2; i += 2) @@ -1069,11 +1284,11 @@ static void newstats(struct cgpu_info *cgpu) void update_usb_stats(__maybe_unused struct cgpu_info *cgpu) { #if DO_USB_STATS - if (cgpu->usbstat < 1) + if (cgpu->usbinfo.usbstat < 1) newstats(cgpu); // we don't know the device_id until after add_cgpu() - usb_stats[cgpu->usbstat - 1].device_id = cgpu->device_id; + usb_stats[cgpu->usbinfo.usbstat - 1].device_id = cgpu->device_id; #endif } @@ -1084,10 +1299,10 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev double diff; int item; - if (cgpu->usbstat < 1) + if (cgpu->usbinfo.usbstat < 1) newstats(cgpu); - details = &(usb_stats[cgpu->usbstat - 1].details[cmd * 2 + seq]); + details = &(usb_stats[cgpu->usbinfo.usbstat - 1].details[cmd * 2 + seq]); diff = tdiff(tv_finish, tv_start); @@ -1116,62 +1331,130 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev memcpy(&(details->item[item].last), tv_start, sizeof(tv_start)); details->item[item].count++; } + +static void rejected_inc(struct cgpu_info *cgpu) +{ + struct cg_usb_stats_details *details; + int item = CMD_ERROR; + + if (cgpu->usbinfo.usbstat < 1) + newstats(cgpu); + + details = &(usb_stats[cgpu->usbinfo.usbstat - 1].details[C_REJECTED * 2 + 0]); + + details->item[item].count++; +} #endif -int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds cmd) +int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds cmd, bool ftdi) { struct cg_usb_device *usbdev = cgpu->usbdev; #if DO_USB_STATS - struct timeval tv_start, tv_finish; + struct timeval tv_start; #endif - int err, got, tot; + struct timeval read_start, tv_finish; + unsigned int initial_timeout; + double max, done; + int err, got, tot, i; bool first = true; + if (cgpu->usbinfo.nodev) { + *buf = '\0'; + *processed = 0; +#if DO_USB_STATS + rejected_inc(cgpu); +#endif + return LIBUSB_ERROR_NO_DEVICE; + } + + if (timeout == DEVTIMEOUT) + timeout = usbdev->found->timeout; + if (eol == -1) { got = 0; STATS_TIMEVAL(&tv_start); err = libusb_bulk_transfer(usbdev->handle, usbdev->found->eps[ep].ep, (unsigned char *)buf, - bufsiz, &got, - timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout); + bufsiz, &got, timeout); STATS_TIMEVAL(&tv_finish); USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, SEQ0); + if (ftdi) { + // first 2 bytes returned are an FTDI status + if (got > 2) { + got -= 2; + memmove(buf, buf+2, got+1); + } else { + got = 0; + *buf = '\0'; + } + } + *processed = got; + if (NODEV(err)) + release_cgpu(cgpu); + return err; } tot = 0; err = LIBUSB_SUCCESS; + initial_timeout = timeout; + max = ((double)timeout) / 1000.0; + gettimeofday(&read_start, NULL); while (bufsiz) { got = 0; STATS_TIMEVAL(&tv_start); err = libusb_bulk_transfer(usbdev->handle, usbdev->found->eps[ep].ep, (unsigned char *)buf, - 1, &got, - timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout); - STATS_TIMEVAL(&tv_finish); + bufsiz, &got, timeout); + gettimeofday(&tv_finish, NULL); USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, first ? SEQ0 : SEQ1); + if (ftdi) { + // first 2 bytes returned are an FTDI status + if (got > 2) { + got -= 2; + memmove(buf, buf+2, got+1); + } else { + got = 0; + *buf = '\0'; + } + } + tot += got; if (err) break; - if (eol == buf[0]) - break; + // WARNING - this will return data past EOL ('if' there is extra data) + for (i = 0; i < got; i++) + if (buf[i] == eol) + goto goteol; buf += got; bufsiz -= got; first = false; + + done = tdiff(&tv_finish, &read_start); + // N.B. this is return LIBUSB_SUCCESS with whatever size has already been read + if (unlikely(done >= max)) + break; + + timeout = initial_timeout - (done * 1000); } +goteol: + *processed = tot; + if (NODEV(err)) + release_cgpu(cgpu); + return err; } @@ -1183,6 +1466,14 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr #endif int err, sent; + if (cgpu->usbinfo.nodev) { + *processed = 0; +#if DO_USB_STATS + rejected_inc(cgpu); +#endif + return LIBUSB_ERROR_NO_DEVICE; + } + sent = 0; STATS_TIMEVAL(&tv_start); err = libusb_bulk_transfer(usbdev->handle, @@ -1195,6 +1486,37 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr *processed = sent; + if (NODEV(err)) + release_cgpu(cgpu); + + return err; +} + +int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout, enum usb_cmds cmd) +{ + struct cg_usb_device *usbdev = cgpu->usbdev; +#if DO_USB_STATS + struct timeval tv_start, tv_finish; +#endif + int err; + + if (cgpu->usbinfo.nodev) { +#if DO_USB_STATS + rejected_inc(cgpu); +#endif + return LIBUSB_ERROR_NO_DEVICE; + } + + STATS_TIMEVAL(&tv_start); + err = libusb_control_transfer(usbdev->handle, request_type, + bRequest, wValue, wIndex, NULL, 0, + timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout); + STATS_TIMEVAL(&tv_finish); + USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, SEQ0); + + if (NODEV(err)) + release_cgpu(cgpu); + return err; } diff --git a/usbutils.h b/usbutils.h index 71fd9699..1ec19360 100644 --- a/usbutils.h +++ b/usbutils.h @@ -1,5 +1,5 @@ /* - * Copyright 2012 Andrew Smith + * Copyright 2012-2013 Andrew Smith * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -12,6 +12,29 @@ #include +// for 0x0403/0x6014 FT232H (and possibly others?) +#define FTDI_TYPE_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT) + +#define FTDI_REQUEST_RESET ((uint8_t)0) +#define FTDI_REQUEST_MODEM ((uint8_t)1) +#define FTDI_REQUEST_FLOW ((uint8_t)2) +#define FTDI_REQUEST_BAUD ((uint8_t)3) +#define FTDI_REQUEST_DATA ((uint8_t)4) + +#define FTDI_VALUE_RESET 0 +#define FTDI_VALUE_PURGE_RX 1 +#define FTDI_VALUE_PURGE_TX 2 + +// baud with a 0 divisor is 120,000,000/10 +//#define FTDI_VALUE_BAUD (0) +//#define FTDI_INDEX_BAUD (0) +#define FTDI_VALUE_BAUD 0xc068 +#define FTDI_INDEX_BAUD 0x0200 + +#define FTDI_VALUE_DATA 0 +#define FTDI_VALUE_FLOW 0 +#define FTDI_VALUE_MODEM 0x0303 + // Use the device defined timeout #define DEVTIMEOUT 0 @@ -32,6 +55,7 @@ struct usb_find_devices { const char *name; uint16_t idVendor; uint16_t idProduct; + int kernel; int config; int interface; unsigned int timeout; @@ -44,8 +68,6 @@ struct cg_usb_device { libusb_device_handle *handle; pthread_mutex_t *mutex; struct libusb_device_descriptor *descriptor; - uint8_t bus_number; - uint8_t device_address; uint16_t usbver; int speed; char *prod_string; @@ -55,8 +77,18 @@ struct cg_usb_device { unsigned char interfaceVersion; // ?? }; +struct cg_usb_info { + uint8_t bus_number; + uint8_t device_address; + int usbstat; + bool nodev; + int nodev_count; + struct timeval last_nodev; +}; + enum usb_cmds { - C_PING = 0, + C_REJECTED = 0, + C_PING, C_CLEAR, C_REQUESTVERSION, C_GETVERSION, @@ -78,32 +110,48 @@ enum usb_cmds { C_SENDWORKSTATUS, C_REQUESTWORKSTATUS, C_GETWORKSTATUS, + C_REQUESTIDENTIFY, + C_GETIDENTIFY, + C_REQUESTFLASH, + C_REQUESTSENDWORK, + C_REQUESTSENDWORKSTATUS, + C_RESET, + C_SETBAUD, + C_SETDATA, + C_SETFLOW, + C_SETMODEM, + C_PURGERX, + C_PURGETX, C_MAX }; -struct device_api; +struct device_drv; struct cgpu_info; void usb_uninit(struct cgpu_info *cgpu); bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found); -void usb_detect(struct device_api *api, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *)); +void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *)); struct api_data *api_usb_stats(int *count); void update_usb_stats(struct cgpu_info *cgpu); -int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds); +int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds, bool ftdi); int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds); +int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout, enum usb_cmds cmd); void usb_cleanup(); #define usb_read(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, -1, cmd) + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, -1, cmd, false) + +#define usb_read_nl(cgpu, buf, bufsiz, read, cmd) \ + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, '\n', cmd, false) #define usb_read_ep(cgpu, ep, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, -1, cmd) + _usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, -1, cmd, false) #define usb_read_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, -1, cmd) + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, -1, cmd, false) #define usb_read_ep_timeout(cgpu, ep, buf, bufsiz, read, timeout, cmd) \ - _usb_read(cgpu, ep, buf, bufsiz, read, timeout, -1, cmd) + _usb_read(cgpu, ep, buf, bufsiz, read, timeout, -1, cmd, false) #define usb_write(cgpu, buf, bufsiz, wrote, cmd) \ _usb_write(cgpu, DEFAULT_EP_OUT, buf, bufsiz, wrote, DEVTIMEOUT, cmd) @@ -117,4 +165,10 @@ void usb_cleanup(); #define usb_write_ep_timeout(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) \ _usb_write(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) +#define usb_ftdi_read_nl(cgpu, buf, bufsiz, read, cmd) \ + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, '\n', cmd, true) + +#define usb_transfer(cgpu, typ, req, val, idx, cmd) \ + _usb_transfer(cgpu, typ, req, val, idx, DEVTIMEOUT, cmd) + #endif diff --git a/util.c b/util.c index 282b7494..056984e1 100644 --- a/util.c +++ b/util.c @@ -261,6 +261,29 @@ static void set_nettime(void) wr_unlock(&netacc_lock); } +static int curl_debug_cb(__maybe_unused CURL *handle, curl_infotype type, + __maybe_unused char *data, size_t size, void *userdata) +{ + struct pool *pool = (struct pool *)userdata; + + switch(type) { + case CURLINFO_HEADER_IN: + case CURLINFO_DATA_IN: + case CURLINFO_SSL_DATA_IN: + pool->cgminer_pool_stats.net_bytes_received += size; + break; + case CURLINFO_HEADER_OUT: + case CURLINFO_DATA_OUT: + case CURLINFO_SSL_DATA_OUT: + pool->cgminer_pool_stats.net_bytes_sent += size; + break; + case CURLINFO_TEXT: + default: + break; + } + return 0; +} + json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass, const char *rpc_req, bool probe, bool longpoll, int *rolltime, @@ -287,10 +310,11 @@ json_t *json_rpc_call(CURL *curl, const char *url, probing = !pool->probed; curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); -#if 0 /* Disable curl debugging since it spews to stderr */ - if (opt_protocol) - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); -#endif + // CURLOPT_VERBOSE won't write to stderr if we use CURLOPT_DEBUGFUNCTION + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_debug_cb); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)pool); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_ENCODING, ""); @@ -912,6 +936,7 @@ static bool __stratum_send(struct pool *pool, char *s, ssize_t len) pool->cgminer_pool_stats.times_sent++; pool->cgminer_pool_stats.bytes_sent += ssent; + pool->cgminer_pool_stats.net_bytes_sent += ssent; return true; } @@ -1041,6 +1066,7 @@ char *recv_line(struct pool *pool) pool->cgminer_pool_stats.times_received++; pool->cgminer_pool_stats.bytes_received += len; + pool->cgminer_pool_stats.net_bytes_received += len; out: if (!sret) clear_sock(pool);
'; - if ($prev !== null) - echo riginput($prev, 'Prev').' '; - echo " "; - if ($next !== null) - echo riginput($next, 'Next').' '; - echo ' '; - if (count($rigs) > 1) - echo " "; + if ($userlist === null || isset($_SESSION[$ses])) + { + if ($prev !== null) + echo riginput($prev, 'Prev').' '; + echo " "; + if ($next !== null) + echo riginput($next, 'Next').' '; + echo ' '; + if (count($rigs) > 1) + echo " "; + } if ($allowcustompages === true) - foreach ($customsummarypages as $pagename => $data) + { + if ($userlist === null || isset($_SESSION[$ses])) + $list = $customsummarypages; + else + { + if ($userlist !== null && isset($userlist['def'])) + $list = array_flip($userlist['def']); + else + $list = array(); + } + + foreach ($list as $pagename => $data) echo " "; + } echo ' '; if ($rig !== null && $readonly === false) @@ -1580,6 +1635,12 @@ function pagebuttons($rig, $pg) echo " "; } refreshbuttons(); + if (isset($_SESSION[$ses])) + echo " "; + else + if ($userlist !== null) + echo " "; + echo "
 
+
+ + +
+ + +
+ + + + + + + + +
 
+

LOGIN

Username:
+
Password:
+
+
+
No rigs defined