diff --git a/Makefile.am b/Makefile.am index e93f8272..2417fbe3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -100,6 +100,10 @@ if HAS_ICARUS cgminer_SOURCES += driver-icarus.c endif +if HAS_AVALON +cgminer_SOURCES += driver-avalon.c +endif + if HAS_MODMINER cgminer_SOURCES += driver-modminer.c bitstreamsdir = $(bindir)/bitstreams diff --git a/README b/README index ab7971d5..deb88d78 100644 --- a/README +++ b/README @@ -984,6 +984,10 @@ Q: Can I mine with Nvidia or Intel GPUs? A: Yes but their hashrate is very poor and likely you'll be using much more energy than you'll be earning in coins. +Q: Can I mine on both Nvidia and AMD GPUs at the same time? +A: No, you must run one instance of cgminer with the --gpu-platform option for +each. + Q: Can I mine on Linux without running Xorg? A: With Nvidia you can, but with AMD you cannot. @@ -1001,13 +1005,51 @@ A: You are generating garbage hashes due to your choice of settings. Your Work Utility (WU) value will confirm you are not generating garbage. You should be getting about .9WU per kHash. If not, then try decreasing your intensity, do not increase the number of gpu-threads, and consider adding -system RAM to match your GPU ram. +system RAM to match your GPU ram. You may also be using a bad combination +of driver and/or SDK. Q: Scrypt fails to initialise the kernel every time? A: Your parameters are too high. Don't add GPU threads, don't set intensity too high, decrease thread concurrency. See the SCRYPT-README for a lot more help. +Q: Cgminer stops mining (or my GPUs go DEAD) and I can't close it? +A: Once the driver has crashed, there is no way for cgminer to close cleanly. +You will have to kill it, and depending on how corrupted your driver state +has gotten, you may even need to reboot. Windows is known to reset drivers +when they fail and cgminer will be stuck trying to use the old driver instance. + +Q: I can't get any monitoring of temperatures or fanspeed with cgminer when +I start it remotely? +A: With linux, make sure to export the DISPLAY variable. On windows, you +cannot access these monitoring values via RDP. This should work with tightVNC +or teamviewer though. + +Q: I change my GPU engine/memory/voltage and cgminer reports back no change? +A: Cgminer asks the GPU using the ATI Display Library to change settings, but +the driver and hardware are free to do what it wants with that query, including +ignoring it. Some GPUs are locked with one or more of those properties as well. + +Q: I have multiple GPUs and although many devices show up, it appears to be +working only on one GPU splitting it up. +A: Your driver setup is failing to properly use the accessory GPUs. Your +driver may be configured wrong or you have a driver version that needs a dummy +plug on all the GPUs that aren't connected to a monitor. + +Q: I have some random GPU performance related problem not addressed above. +A: Seriously, it's the driver and/or SDK. Uninstall them and start again, +noting there is no clean way to uninstall them so you have to use extra tools +or do it manually. + +Q: Do I need to recompile after updating my driver/SDK? +A: No. The software is unchanged regardless of which driver/SDK/ADL_SDK version +you are running. However if you change SDKs you should delete any generated +.bin files for them to be recreated with the new SDK. + +Q: I switch users on windows and my mining stops working? +A: That's correct, it does. It's a permissions issue that there is no known +fix for. + Q: My network gets slower and slower and then dies for a minute? A; Try the --net-delay option. diff --git a/api-example.c b/api-example.c index 9bd44791..71b5b002 100644 --- a/api-example.c +++ b/api-example.c @@ -145,7 +145,7 @@ static const char SEPARATOR = '|'; static const char COMMA = ','; static const char EQ = '='; -static int ONLY = 0; +static int ONLY; void display(char *buf) { diff --git a/api.c b/api.c index a2c70582..24dcb589 100644 --- a/api.c +++ b/api.c @@ -29,7 +29,7 @@ #include "util.h" #include "driver-cpu.h" /* for algo_names[], TODO: re-factor dependency */ -#if defined(USE_BFLSC) +#if defined(USE_BFLSC) || defined(USE_AVALON) #define HAVE_AN_ASIC 1 #endif @@ -179,6 +179,9 @@ static const char *DEVICECODE = "" #ifdef USE_ICARUS "ICA " #endif +#ifdef USE_AVALON + "AVA " +#endif #ifdef USE_ZTEX "ZTX " #endif @@ -605,9 +608,6 @@ struct CODES { static int my_thr_id = 0; static bool bye; -#if defined(HAVE_OPENCL) || defined (HAVE_AN_ASIC) || defined(HAVE_AN_FPGA) -static bool ping = true; -#endif // Used to control quit restart access to shutdown variables static pthread_mutex_t quit_restart_lock; @@ -1178,6 +1178,10 @@ static int numascs() rd_lock(&devices_lock); for (i = 0; i < total_devices; i++) { +#ifdef USE_AVALON + if (devices[i]->drv->drv_id == DRIVER_AVALON) + count++; +#endif #ifdef USE_BFLSC if (devices[i]->drv->drv_id == DRIVER_BFLSC) count++; @@ -1194,6 +1198,10 @@ static int ascdevice(int ascid) rd_lock(&devices_lock); for (i = 0; i < total_devices; i++) { +#ifdef USE_AVALON + if (devices[i]->drv->drv_id == DRIVER_AVALON) + count++; +#endif #ifdef USE_BFLSC if (devices[i]->drv->drv_id == DRIVER_BFLSC) count++; diff --git a/cgminer.c b/cgminer.c index 7816af3c..f2840f0d 100644 --- a/cgminer.c +++ b/cgminer.c @@ -48,6 +48,7 @@ #include "driver-opencl.h" #include "bench_block.h" #include "scrypt.h" +#include "driver-avalon.h" #if defined(unix) #include @@ -55,9 +56,9 @@ #include #endif -#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_MODMINER) +#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_AVALON) || defined(USE_MODMINER) # define USE_FPGA -#if defined(USE_ICARUS) +#if defined(USE_ICARUS) || defined(USE_AVALON) # define USE_FPGA_SERIAL #endif #elif defined(USE_ZTEX) @@ -138,6 +139,9 @@ bool opt_disable_pool; char *opt_icarus_options = NULL; char *opt_icarus_timing = NULL; bool opt_worktime; +#ifdef USE_AVALON +char *opt_avalon_options = NULL; +#endif #ifdef USE_USBUTILS char *opt_usb_select = NULL; int opt_usbdump = -1; @@ -394,6 +398,7 @@ struct thr_info *get_thread(int thr_id) rd_lock(&mining_thr_lock); thr = mining_thr[thr_id]; rd_unlock(&mining_thr_lock); + return thr; } @@ -411,6 +416,7 @@ struct cgpu_info *get_devices(int id) rd_lock(&devices_lock); cgpu = devices[id]; rd_unlock(&devices_lock); + return cgpu; } @@ -451,6 +457,7 @@ static void sharelog(const char*disposition, const struct work*work) ret = fwrite(s, rv, 1, sharelog_file); fflush(sharelog_file); mutex_unlock(&sharelog_lock); + if (ret != 1) applog(LOG_ERR, "sharelog fwrite error"); } @@ -496,6 +503,7 @@ static bool pool_tset(struct pool *pool, bool *var) ret = *var; *var = true; mutex_unlock(&pool->pool_lock); + return ret; } @@ -507,6 +515,7 @@ bool pool_tclear(struct pool *pool, bool *var) ret = *var; *var = false; mutex_unlock(&pool->pool_lock); + return ret; } @@ -517,6 +526,7 @@ struct pool *current_pool(void) cg_rlock(&control_lock); pool = currentpool; cg_runlock(&control_lock); + return pool; } @@ -786,6 +796,7 @@ static void load_temp_cutoffs() devices[i]->cutofftemp = opt_cutofftemp; } rd_unlock(&devices_lock); + return; } if (device <= 1) { @@ -833,6 +844,15 @@ static char *set_icarus_timing(const char *arg) } #endif +#ifdef USE_AVALON +static char *set_avalon_options(const char *arg) +{ + opt_set_charp(arg, &opt_avalon_options); + + return NULL; +} +#endif + #ifdef USE_USBUTILS static char *set_usb_select(const char *arg) { @@ -1032,6 +1052,11 @@ static struct opt_table opt_config_table[] = { OPT_WITH_ARG("--icarus-timing", set_icarus_timing, NULL, NULL, opt_hidden), +#endif +#ifdef USE_AVALON + OPT_WITH_ARG("--avalon-options", + set_avalon_options, NULL, NULL, + opt_hidden), #endif OPT_WITHOUT_ARG("--load-balance", set_loadbalance, &pool_strategy, @@ -1105,7 +1130,7 @@ static struct opt_table opt_config_table[] = { #ifdef USE_FPGA_SERIAL OPT_WITH_ARG("--scan-serial|-S", add_serial, NULL, NULL, - "Serial port to probe for FPGA Mining device"), + "Serial port to probe for Icarus FPGA Mining device"), #endif OPT_WITH_ARG("--scan-time|-s", set_int_0_to_9999, opt_show_intval, &opt_scantime, @@ -1359,6 +1384,9 @@ static char *opt_verusage_and_exit(const char *extra) #ifdef USE_ICARUS "icarus " #endif +#ifdef USE_AVALON + "avalon " +#endif #ifdef USE_MODMINER "modminer " #endif @@ -1470,9 +1498,11 @@ static struct work *make_work(void) if (unlikely(!work)) quit(1, "Failed to calloc work in make_work"); + cg_wlock(&control_lock); work->id = total_work++; cg_wunlock(&control_lock); + return work; } @@ -1865,6 +1895,7 @@ static int total_staged(void) mutex_lock(stgd_lock); ret = __total_staged(); mutex_unlock(stgd_lock); + return ret; } @@ -2040,10 +2071,8 @@ static void curses_print_status(void) pool->has_gbt ? "GBT" : "LP", pool->rpc_user); } wclrtoeol(statuswin); - cg_rlock(&ch_lock); mvwprintw(statuswin, 5, 0, " Block: %s... Diff:%s Started: %s Best share: %s ", current_hash, block_diff, blocktime, best_share); - cg_runlock(&ch_lock); mvwhline(statuswin, 6, 0, '-', 80); mvwhline(statuswin, statusy - 1, 0, '-', 80); mvwprintw(statuswin, devcursor - 1, 1, "[P]ool management %s[S]ettings [D]isplay options [Q]uit", @@ -2916,7 +2945,6 @@ static void recruit_curl(struct pool *pool) list_add(&ce->node, &pool->curlring); pool->curls++; - applog(LOG_DEBUG, "Recruited curl %d for pool %d", pool->curls, pool->pool_no); } /* Grab an available curl if there is one. If not, then recruit extra curls @@ -2927,23 +2955,29 @@ static void recruit_curl(struct pool *pool) static struct curl_ent *pop_curl_entry(struct pool *pool) { int curl_limit = opt_delaynet ? 5 : (mining_threads + opt_queue) * 2; + bool recruited = false; struct curl_ent *ce; mutex_lock(&pool->pool_lock); retry: - if (!pool->curls) + if (!pool->curls) { recruit_curl(pool); - else if (list_empty(&pool->curlring)) { + recruited = true; + } else if (list_empty(&pool->curlring)) { if (pool->curls >= curl_limit) { pthread_cond_wait(&pool->cr_cond, &pool->pool_lock); goto retry; - } else + } else { recruit_curl(pool); + recruited = true; + } } ce = list_entry(pool->curlring.next, struct curl_ent, node); list_del(&ce->node); mutex_unlock(&pool->pool_lock); + if (recruited) + applog(LOG_DEBUG, "Recruited curl for pool %d", pool->pool_no); return ce; } @@ -3073,7 +3107,6 @@ static bool clone_available(void) roll_work(work); work_clone = make_clone(work); roll_work(work); - applog(LOG_DEBUG, "Pushing cloned available work to stage thread"); cloned = true; break; } @@ -3082,8 +3115,10 @@ static bool clone_available(void) out_unlock: mutex_unlock(stgd_lock); - if (cloned) + if (cloned) { + applog(LOG_DEBUG, "Pushing cloned available work to stage thread"); stage_work(work_clone); + } return cloned; } @@ -3133,10 +3168,12 @@ static bool stale_work(struct work *work, bool share) } same_job = true; + cg_rlock(&pool->data_lock); if (strcmp(work->job_id, pool->swork.job_id)) same_job = false; cg_runlock(&pool->data_lock); + if (!same_job) { applog(LOG_DEBUG, "Work stale due to stratum job_id mismatch"); return true; @@ -3180,6 +3217,7 @@ static uint64_t share_diff(const struct work *work) if (unlikely(!d64)) d64 = 1; ret = diffone / d64; + cg_wlock(&control_lock); if (ret > best_diff) { best_diff = ret; @@ -3188,6 +3226,7 @@ static uint64_t share_diff(const struct work *work) if (ret > work->pool->best_diff) work->pool->best_diff = ret; cg_wunlock(&control_lock); + return ret; } @@ -3252,6 +3291,7 @@ static void *submit_work_thread(void *userdata) total_diff_stale += work->work_difficulty; pool->diff_stale += work->work_difficulty; mutex_unlock(&stats_lock); + goto out; } work->stale = true; @@ -3291,10 +3331,12 @@ static void *submit_work_thread(void *userdata) if (likely(stratum_send(pool, s, strlen(s)))) { if (pool_tclear(pool, &pool->submit_fail)) applog(LOG_WARNING, "Pool %d communication resumed, submitting work", pool->pool_no); + mutex_lock(&sshare_lock); HASH_ADD_INT(stratum_shares, id, sshare); pool->sshares++; mutex_unlock(&sshare_lock); + applog(LOG_DEBUG, "Successfully submitted, adding to stratum_shares db"); submitted = true; break; @@ -3340,6 +3382,7 @@ static void *submit_work_thread(void *userdata) total_diff_stale += work->work_difficulty; pool->diff_stale += work->work_difficulty; mutex_unlock(&stats_lock); + break; } @@ -3567,8 +3610,9 @@ static void set_curblock(char *hexstr, unsigned char *hash) free(current_fullhash); current_fullhash = bin2hex(block_hash_swap, 32); get_timestamp(blocktime, &block_timeval); - applog(LOG_INFO, "New block: %s... diff %s", current_hash, block_diff); cg_wunlock(&ch_lock); + + applog(LOG_INFO, "New block: %s... diff %s", current_hash, block_diff); } /* Search to see if this string is from a block that has been seen before */ @@ -3579,6 +3623,7 @@ static bool block_exists(char *hexstr) rd_lock(&blk_lock); HASH_FIND_STR(blocks, hexstr, s); rd_unlock(&blk_lock); + if (s) return true; return false; @@ -3667,6 +3712,7 @@ static bool test_work_current(struct work *work) quit (1, "test_work_current OOM"); strcpy(s->hash, hexstr); s->block_no = new_blocks++; + wr_lock(&blk_lock); /* Only keep the last hour's worth of blocks in memory since * work from blocks before this is virtually impossible and we @@ -3683,6 +3729,7 @@ static bool test_work_current(struct work *work) HASH_ADD_STR(blocks, hash, s); set_blockdiff(work); wr_unlock(&blk_lock); + if (deleted_block) applog(LOG_DEBUG, "Deleted block %d from database", deleted_block); set_curblock(hexstr, work->data); @@ -4675,6 +4722,7 @@ static void hashmeter(int thr_id, struct timeval *diff, local_mhashes_done = 0; out_unlock: mutex_unlock(&hash_lock); + if (showlog) { if (!curses_active) { printf("%s \r", statusline); @@ -4737,6 +4785,7 @@ static bool parse_stratum_response(struct pool *pool, char *s) } id = json_integer_value(id_val); + mutex_lock(&sshare_lock); HASH_FIND_INT(stratum_shares, &id, sshare); if (sshare) { @@ -4744,6 +4793,7 @@ static bool parse_stratum_response(struct pool *pool, char *s) pool->sshares--; } mutex_unlock(&sshare_lock); + if (!sshare) { if (json_is_true(res_val)) applog(LOG_NOTICE, "Accepted untracked stratum share from pool %d", pool->pool_no); @@ -4814,6 +4864,7 @@ static int cp_prio(void) cg_rlock(&control_lock); prio = currentpool->prio; cg_runlock(&control_lock); + return prio; } @@ -4875,6 +4926,7 @@ static bool supports_resume(struct pool *pool) cg_rlock(&pool->data_lock); ret = (pool->sessionid != NULL); cg_runlock(&pool->data_lock); + return ret; } @@ -5707,15 +5759,20 @@ static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct de { thread_reportout(mythr); do { - struct work *work; + bool need_work; + + rd_lock(&cgpu->qlock); + need_work = (HASH_COUNT(cgpu->queued_work) == cgpu->queued_count); + rd_unlock(&cgpu->qlock); + + if (need_work) { + struct work *work = get_work(mythr, thr_id); - wr_lock(&cgpu->qlock); - if (HASH_COUNT(cgpu->queued_work) == cgpu->queued_count) { - work = get_work(mythr, thr_id); work->device_diff = MIN(drv->max_diff, work->work_difficulty); + wr_lock(&cgpu->qlock); HASH_ADD_INT(cgpu->queued_work, id, work); + wr_unlock(&cgpu->qlock); } - 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. */ @@ -5789,6 +5846,7 @@ void work_completed(struct cgpu_info *cgpu, struct work *work) cgpu->queued_count--; HASH_DEL(cgpu->queued_work, work); wr_unlock(&cgpu->qlock); + free_work(work); } @@ -6129,6 +6187,7 @@ static void reap_curl(struct pool *pool) int reaped = 0; gettimeofday(&now, NULL); + mutex_lock(&pool->pool_lock); list_for_each_entry_safe(ent, iter, &pool->curlring, node) { if (pool->curls < 2) @@ -6142,6 +6201,7 @@ static void reap_curl(struct pool *pool) } } mutex_unlock(&pool->pool_lock); + if (reaped) applog(LOG_DEBUG, "Reaped %d curl%s from pool %d", reaped, reaped > 1 ? "s" : "", pool->pool_no); } @@ -6264,6 +6324,7 @@ 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; + rd_lock(&mining_thr_lock); for (i = 0; i < mining_threads; i++) mining_thr[i]->pause = true; @@ -6538,15 +6599,20 @@ static void *test_pool_thread(void *arg) if (pool_active(pool, false)) { pool_tset(pool, &pool->lagging); pool_tclear(pool, &pool->idle); + bool first_pool = false; cg_wlock(&control_lock); if (!pools_active) { currentpool = pool; if (pool->pool_no != 0) - applog(LOG_NOTICE, "Switching to pool %d %s - first alive pool", pool->pool_no, pool->rpc_url); + first_pool = true; pools_active = true; } cg_wunlock(&control_lock); + + if (unlikely(first_pool)) + applog(LOG_NOTICE, "Switching to pool %d %s - first alive pool", pool->pool_no, pool->rpc_url); + pool_resus(pool); } else pool_died(pool); @@ -6751,6 +6817,10 @@ extern struct device_drv bitforce_drv; extern struct device_drv icarus_drv; #endif +#ifdef USE_AVALON +extern struct device_drv avalon_drv; +#endif + #ifdef USE_MODMINER extern struct device_drv modminer_drv; #endif @@ -6856,9 +6926,11 @@ void fill_device_drv(struct cgpu_info *cgpu) void enable_device(struct cgpu_info *cgpu) { cgpu->deven = DEV_ENABLED; + wr_lock(&devices_lock); devices[cgpu->cgminer_id = cgminer_id_count++] = cgpu; wr_unlock(&devices_lock); + if (hotplug_mode) { new_threads += cgpu->threads; #ifdef HAVE_CURSES @@ -6901,9 +6973,11 @@ bool add_cgpu(struct cgpu_info*cgpu) cgpu->device_id = d->lastid = 0; HASH_ADD_STR(devids, name, d); } + wr_lock(&devices_lock); devices = realloc(devices, sizeof(struct cgpu_info *) * (total_devices + new_devices + 2)); wr_unlock(&devices_lock); + if (hotplug_mode) devices[total_devices + new_devices++] = cgpu; else @@ -6942,6 +7016,7 @@ static void hotplug_process() wr_lock(&mining_thr_lock); mining_thr = realloc(mining_thr, sizeof(thr) * (mining_threads + new_threads + 1)); wr_unlock(&mining_thr_lock); + if (!mining_thr) quit(1, "Failed to hotplug realloc mining_thr"); for (i = 0; i < new_threads; i++) { @@ -7270,6 +7345,11 @@ int main(int argc, char *argv[]) icarus_drv.drv_detect(); #endif +#ifdef USE_AVALON + if (!opt_scrypt) + avalon_drv.drv_detect(); +#endif + #ifdef USE_BFLSC if (!opt_scrypt) bflsc_drv.drv_detect(); diff --git a/configure.ac b/configure.ac index 124cdd0f..6c2b3c40 100644 --- a/configure.ac +++ b/configure.ac @@ -252,6 +252,17 @@ if test "x$icarus" = xyes; then fi AM_CONDITIONAL([HAS_ICARUS], [test x$icarus = xyes]) +avalon="no" + +AC_ARG_ENABLE([avalon], + [AC_HELP_STRING([--enable-avalon],[Compile support for Avalon (default disabled)])], + [avalon=$enableval] + ) +if test "x$avalon" = xyes; then + AC_DEFINE([USE_AVALON], [1], [Defined to 1 if Avalon support is wanted]) +fi +AM_CONDITIONAL([HAS_AVALON], [test x$avalon = xyes]) + modminer="no" AC_ARG_ENABLE([modminer], @@ -298,7 +309,7 @@ else ]) fi -AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$ztex != xnononono]) +AM_CONDITIONAL([NEED_FPGAUTILS], [test x$avalon$icarus$bitforce$modminer$ztex != xnonononono]) AM_CONDITIONAL([NEED_USBUTILS_C], [test x$bitforce$modminer$bflsc != xnonono]) AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes]) AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue]) @@ -523,14 +534,14 @@ if test "x$opencl" != xno; then else echo " OpenCL...............: NOT FOUND. GPU mining support DISABLED" - if test "x$cpumining$bitforce$icarus$ztex$modminer$bflsc" = xnononononono; then + if test "x$cpumining$bitforce$avalon$icarus$ztex$modminer$bflsc" = xnonononononono; then AC_MSG_ERROR([No mining configured in]) fi echo " scrypt...............: Disabled (needs OpenCL)" fi else echo " OpenCL...............: Detection overrided. GPU mining support DISABLED" - if test "x$cpumining$bitforce$icarus$ztex$modminer$bflsc" = xnononononono; then + if test "x$cpumining$bitforce$icarus$avalon$ztex$modminer$bflsc" = xnonononononono; then AC_MSG_ERROR([No mining configured in]) fi echo " scrypt...............: Disabled (needs OpenCL)" @@ -547,6 +558,12 @@ else fi echo +if test "x$avalon" = xyes; then + echo " Avalon.ASICs.........: Enabled" +else + echo " Avalon.ASICs.........: Disabled" +fi + if test "x$bflsc" = xyes; then echo " BFL.ASICs............: Enabled" else diff --git a/driver-avalon.c b/driver-avalon.c new file mode 100644 index 00000000..4ca39528 --- /dev/null +++ b/driver-avalon.c @@ -0,0 +1,1038 @@ +/* + * Copyright 2013 Con Kolivas + * Copyright 2012-2013 Xiangfu + * Copyright 2012 Luke Dashjr + * Copyright 2012 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 + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef WIN32 + #include + #include + #include + #ifndef O_CLOEXEC + #define O_CLOEXEC 0 + #endif +#else + #include + #include +#endif + +#include "elist.h" +#include "miner.h" +#include "fpgautils.h" +#include "driver-avalon.h" +#include "hexdump.c" + +static int option_offset = -1; +struct avalon_info **avalon_infos; +struct device_drv avalon_drv; + +static int avalon_init_task(struct avalon_task *at, + uint8_t reset, uint8_t ff, uint8_t fan, + uint8_t timeout, uint8_t asic_num, + uint8_t miner_num, uint8_t nonce_elf, + uint8_t gate_miner, int frequency) +{ + uint8_t *buf; + static bool first = true; + + if (unlikely(!at)) + return -1; + + if (unlikely(timeout <= 0 || asic_num <= 0 || miner_num <= 0)) + return -1; + + memset(at, 0, sizeof(struct avalon_task)); + + if (unlikely(reset)) { + at->reset = 1; + at->fan_eft = 1; + at->timer_eft = 1; + first = true; + } + + at->flush_fifo = (ff ? 1 : 0); + at->fan_eft = (fan ? 1 : 0); + + if (unlikely(first && !at->reset)) { + at->fan_eft = 1; + at->timer_eft = 1; + first = false; + } + + at->fan_pwm_data = (fan ? fan : AVALON_DEFAULT_FAN_MAX_PWM); + at->timeout_data = timeout; + at->asic_num = asic_num; + at->miner_num = miner_num; + at->nonce_elf = nonce_elf; + + at->gate_miner_elf = 1; + at->asic_pll = 1; + + if (unlikely(gate_miner)) { + at-> gate_miner = 1; + at->asic_pll = 0; + } + + buf = (uint8_t *)at; + buf[5] = 0x00; + buf[8] = 0x74; + buf[9] = 0x01; + buf[10] = 0x00; + buf[11] = 0x00; + if (frequency == 256) { + buf[6] = 0x03; + buf[7] = 0x08; + } else if (frequency == 270) { + buf[6] = 0x73; + buf[7] = 0x08; + } else if (frequency == 282) { + buf[6] = 0xd3; + buf[7] = 0x08; + } else if (frequency == 300) { + buf[6] = 0x63; + buf[7] = 0x09; + } + + return 0; +} + +static inline void avalon_create_task(struct avalon_task *at, + struct work *work) +{ + memcpy(at->midstate, work->midstate, 32); + memcpy(at->data, work->data + 64, 12); +} + +static int avalon_send_task(int fd, const struct avalon_task *at, + struct cgpu_info *avalon) + +{ + size_t ret; + int full; + struct timespec p; + uint8_t buf[AVALON_WRITE_SIZE + 4 * AVALON_DEFAULT_ASIC_NUM]; + size_t nr_len; + struct avalon_info *info; + uint64_t delay = 32000000; /* Default 32ms for B19200 */ + uint32_t nonce_range; + int i; + + if (at->nonce_elf) + nr_len = AVALON_WRITE_SIZE + 4 * at->asic_num; + else + nr_len = AVALON_WRITE_SIZE; + + memcpy(buf, at, AVALON_WRITE_SIZE); + + if (at->nonce_elf) { + nonce_range = (uint32_t)0xffffffff / at->asic_num; + for (i = 0; i < at->asic_num; i++) { + buf[AVALON_WRITE_SIZE + (i * 4) + 3] = + (i * nonce_range & 0xff000000) >> 24; + buf[AVALON_WRITE_SIZE + (i * 4) + 2] = + (i * nonce_range & 0x00ff0000) >> 16; + buf[AVALON_WRITE_SIZE + (i * 4) + 1] = + (i * nonce_range & 0x0000ff00) >> 8; + buf[AVALON_WRITE_SIZE + (i * 4) + 0] = + (i * nonce_range & 0x000000ff) >> 0; + } + } +#if defined(__BIG_ENDIAN__) || defined(MIPSEB) + uint8_t tt = 0; + + tt = (buf[0] & 0x0f) << 4; + tt |= ((buf[0] & 0x10) ? (1 << 3) : 0); + tt |= ((buf[0] & 0x20) ? (1 << 2) : 0); + tt |= ((buf[0] & 0x40) ? (1 << 1) : 0); + tt |= ((buf[0] & 0x80) ? (1 << 0) : 0); + buf[0] = tt; + + tt = (buf[4] & 0x0f) << 4; + tt |= ((buf[4] & 0x10) ? (1 << 3) : 0); + tt |= ((buf[4] & 0x20) ? (1 << 2) : 0); + tt |= ((buf[4] & 0x40) ? (1 << 1) : 0); + tt |= ((buf[4] & 0x80) ? (1 << 0) : 0); + buf[4] = tt; +#endif + if (likely(avalon)) { + info = avalon_infos[avalon->device_id]; + delay = nr_len * 10 * 1000000000ULL; + delay = delay / info->baud; + } + + if (at->reset) + nr_len = 1; + if (opt_debug) { + applog(LOG_DEBUG, "Avalon: Sent(%d):", nr_len); + hexdump((uint8_t *)buf, nr_len); + } + ret = write(fd, buf, nr_len); + if (unlikely(ret != nr_len)) + return AVA_SEND_ERROR; + + p.tv_sec = 0; + p.tv_nsec = (long)delay + 4000000; + nanosleep(&p, NULL); + applog(LOG_DEBUG, "Avalon: Sent: Buffer delay: %ld", p.tv_nsec); + + full = avalon_buffer_full(fd); + applog(LOG_DEBUG, "Avalon: Sent: Buffer full: %s", + ((full == AVA_BUFFER_FULL) ? "Yes" : "No")); + + if (unlikely(full == AVA_BUFFER_FULL)) + return AVA_SEND_BUFFER_FULL; + + return AVA_SEND_BUFFER_EMPTY; +} + +static inline int avalon_gets(int fd, uint8_t *buf, struct thr_info *thr, + struct timeval *tv_finish) +{ + int read_amount = AVALON_READ_SIZE; + bool first = true; + ssize_t ret = 0; + + while (true) { + struct timeval timeout; + fd_set rd; + + if (unlikely(thr->work_restart)) { + applog(LOG_DEBUG, "Avalon: Work restart"); + return AVA_GETS_RESTART; + } + + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + + FD_ZERO(&rd); + FD_SET(fd, &rd); + ret = select(fd + 1, &rd, NULL, NULL, &timeout); + if (unlikely(ret < 0)) { + applog(LOG_ERR, "Avalon: Error %d on select in avalon_gets", errno); + return AVA_GETS_ERROR; + } + if (ret) { + ret = read(fd, buf, read_amount); + if (unlikely(ret < 0)) { + applog(LOG_ERR, "Avalon: Error %d on read in avalon_gets", errno); + return AVA_GETS_ERROR; + } + if (likely(first)) { + gettimeofday(tv_finish, NULL); + first = false; + } + if (likely(ret >= read_amount)) + return AVA_GETS_OK; + buf += ret; + read_amount -= ret; + continue; + } + + if (unlikely(thr->work_restart)) { + applog(LOG_DEBUG, "Avalon: Work restart"); + return AVA_GETS_RESTART; + } + + return AVA_GETS_TIMEOUT; + } +} + +static int avalon_get_result(int fd, struct avalon_result *ar, + struct thr_info *thr, struct timeval *tv_finish) +{ + uint8_t result[AVALON_READ_SIZE]; + int ret; + + memset(result, 0, AVALON_READ_SIZE); + ret = avalon_gets(fd, result, thr, tv_finish); + + if (ret == AVA_GETS_OK) { + if (opt_debug) { + applog(LOG_DEBUG, "Avalon: get:"); + hexdump((uint8_t *)result, AVALON_READ_SIZE); + } + memcpy((uint8_t *)ar, result, AVALON_READ_SIZE); + } + + return ret; +} + +static bool avalon_decode_nonce(struct thr_info *thr, struct avalon_result *ar, + uint32_t *nonce) +{ + struct cgpu_info *avalon; + struct avalon_info *info; + struct work *work; + + avalon = thr->cgpu; + if (unlikely(!avalon->works)) + return false; + + work = find_queued_work_bymidstate(avalon, (char *)ar->midstate, 32, + (char *)ar->data, 64, 12); + if (!work) + return false; + + info = avalon_infos[avalon->device_id]; + info->matching_work[work->subid]++; + *nonce = htole32(ar->nonce); + submit_nonce(thr, work, *nonce); + + return true; +} + +static void avalon_get_reset(int fd, struct avalon_result *ar) +{ + int read_amount = AVALON_READ_SIZE; + uint8_t result[AVALON_READ_SIZE]; + struct timeval timeout = {1, 0}; + ssize_t ret = 0, offset = 0; + fd_set rd; + + memset(result, 0, AVALON_READ_SIZE); + memset(ar, 0, AVALON_READ_SIZE); + FD_ZERO(&rd); + FD_SET(fd, &rd); + ret = select(fd + 1, &rd, NULL, NULL, &timeout); + if (unlikely(ret < 0)) { + applog(LOG_WARNING, "Avalon: Error %d on select in avalon_get_reset", errno); + return; + } + if (!ret) { + applog(LOG_WARNING, "Avalon: Timeout on select in avalon_get_reset"); + return; + } + do { + ret = read(fd, result + offset, read_amount); + if (unlikely(ret < 0)) { + applog(LOG_WARNING, "Avalon: Error %d on read in avalon_get_reset", errno); + return; + } + read_amount -= ret; + offset += ret; + } while (read_amount > 0); + if (opt_debug) { + applog(LOG_DEBUG, "Avalon: get:"); + hexdump((uint8_t *)result, AVALON_READ_SIZE); + } + memcpy((uint8_t *)ar, result, AVALON_READ_SIZE); +} + +static int avalon_reset(int fd, struct avalon_result *ar) +{ + struct avalon_task at; + uint8_t *buf; + int ret, i = 0; + struct timespec p; + + avalon_init_task(&at, 1, 0, + AVALON_DEFAULT_FAN_MAX_PWM, + AVALON_DEFAULT_TIMEOUT, + AVALON_DEFAULT_ASIC_NUM, + AVALON_DEFAULT_MINER_NUM, + 0, 0, + AVALON_DEFAULT_FREQUENCY); + ret = avalon_send_task(fd, &at, NULL); + if (ret == AVA_SEND_ERROR) + return 1; + + avalon_get_reset(fd, ar); + + buf = (uint8_t *)ar; + /* Sometimes there is one extra 0 byte for some reason in the buffer, + * so work around it. */ + if (buf[0] == 0) + buf = (uint8_t *)(ar + 1); + if (buf[0] == 0xAA && buf[1] == 0x55 && + buf[2] == 0xAA && buf[3] == 0x55) { + for (i = 4; i < 11; i++) + if (buf[i] != 0) + break; + } + + p.tv_sec = 0; + p.tv_nsec = AVALON_RESET_PITCH; + nanosleep(&p, NULL); + + if (i != 11) { + applog(LOG_ERR, "Avalon: Reset failed! not an Avalon?" + " (%d: %02x %02x %02x %02x)", + i, buf[0], buf[1], buf[2], buf[3]); + /* FIXME: return 1; */ + } else + applog(LOG_WARNING, "Avalon: Reset succeeded"); + return 0; +} + +static void avalon_idle(struct cgpu_info *avalon) +{ + int i, ret; + struct avalon_task at; + + int fd = avalon->device_fd; + struct avalon_info *info = avalon_infos[avalon->device_id]; + int avalon_get_work_count = info->miner_count; + + i = 0; + while (true) { + avalon_init_task(&at, 0, 0, info->fan_pwm, + info->timeout, info->asic_count, + info->miner_count, 1, 1, info->frequency); + ret = avalon_send_task(fd, &at, avalon); + if (unlikely(ret == AVA_SEND_ERROR || + (ret == AVA_SEND_BUFFER_EMPTY && + (i + 1 == avalon_get_work_count * 2)))) { + applog(LOG_ERR, "AVA%i: Comms error", avalon->device_id); + return; + } + if (i + 1 == avalon_get_work_count * 2) + break; + + if (ret == AVA_SEND_BUFFER_FULL) + break; + + i++; + } + applog(LOG_ERR, "Avalon: Goto idle mode"); +} + +static void get_options(int this_option_offset, int *baud, int *miner_count, + int *asic_count, int *timeout, int *frequency) +{ + char err_buf[BUFSIZ+1]; + char buf[BUFSIZ+1]; + char *ptr, *comma, *colon, *colon2, *colon3, *colon4; + size_t max; + int i, tmp; + + if (opt_avalon_options == NULL) + buf[0] = '\0'; + else { + ptr = opt_avalon_options; + for (i = 0; i < this_option_offset; i++) { + comma = strchr(ptr, ','); + if (comma == NULL) + break; + ptr = comma + 1; + } + + comma = strchr(ptr, ','); + if (comma == NULL) + max = strlen(ptr); + else + max = comma - ptr; + + if (max > BUFSIZ) + max = BUFSIZ; + strncpy(buf, ptr, max); + buf[max] = '\0'; + } + + *baud = AVALON_IO_SPEED; + *miner_count = AVALON_DEFAULT_MINER_NUM - 8; + *asic_count = AVALON_DEFAULT_ASIC_NUM; + *timeout = AVALON_DEFAULT_TIMEOUT; + *frequency = AVALON_DEFAULT_FREQUENCY; + + if (!(*buf)) + return; + + colon = strchr(buf, ':'); + if (colon) + *(colon++) = '\0'; + + tmp = atoi(buf); + switch (tmp) { + case 115200: + *baud = 115200; + break; + case 57600: + *baud = 57600; + break; + case 38400: + *baud = 38400; + break; + case 19200: + *baud = 19200; + break; + default: + sprintf(err_buf, + "Invalid avalon-options for baud (%s) " + "must be 115200, 57600, 38400 or 19200", buf); + quit(1, err_buf); + } + + if (colon && *colon) { + colon2 = strchr(colon, ':'); + if (colon2) + *(colon2++) = '\0'; + + if (*colon) { + tmp = atoi(colon); + if (tmp > 0 && tmp <= AVALON_DEFAULT_MINER_NUM) { + *miner_count = tmp; + } else { + sprintf(err_buf, + "Invalid avalon-options for " + "miner_count (%s) must be 1 ~ %d", + colon, AVALON_DEFAULT_MINER_NUM); + quit(1, err_buf); + } + } + + if (colon2 && *colon2) { + colon3 = strchr(colon2, ':'); + if (colon3) + *(colon3++) = '\0'; + + tmp = atoi(colon2); + if (tmp > 0 && tmp <= AVALON_DEFAULT_ASIC_NUM) + *asic_count = tmp; + else { + sprintf(err_buf, + "Invalid avalon-options for " + "asic_count (%s) must be 1 ~ %d", + colon2, AVALON_DEFAULT_ASIC_NUM); + quit(1, err_buf); + } + + if (colon3 && *colon3) { + colon4 = strchr(colon3, ':'); + if (colon4) + *(colon4++) = '\0'; + + tmp = atoi(colon3); + if (tmp > 0 && tmp <= 0xff) + *timeout = tmp; + else { + sprintf(err_buf, + "Invalid avalon-options for " + "timeout (%s) must be 1 ~ %d", + colon3, 0xff); + quit(1, err_buf); + } + if (colon4 && *colon4) { + tmp = atoi(colon4); + switch (tmp) { + case 256: + case 270: + case 282: + case 300: + *frequency = tmp; + break; + default: + sprintf(err_buf, + "Invalid avalon-options for " + "frequency must be 256/270/282/300"); + quit(1, err_buf); + } + } + } + } + } +} + +static bool avalon_detect_one(const char *devpath) +{ + struct avalon_info *info; + struct avalon_result ar; + int fd, ret; + int baud, miner_count, asic_count, timeout, frequency = 0; + struct cgpu_info *avalon; + + int this_option_offset = ++option_offset; + get_options(this_option_offset, &baud, &miner_count, &asic_count, + &timeout, &frequency); + + applog(LOG_DEBUG, "Avalon Detect: Attempting to open %s " + "(baud=%d miner_count=%d asic_count=%d timeout=%d frequency=%d)", + devpath, baud, miner_count, asic_count, timeout, frequency); + + fd = avalon_open2(devpath, baud, true); + if (unlikely(fd == -1)) { + applog(LOG_ERR, "Avalon Detect: Failed to open %s", devpath); + return false; + } + + /* We have a real Avalon! */ + avalon = calloc(1, sizeof(struct cgpu_info)); + avalon->drv = &avalon_drv; + avalon->device_path = strdup(devpath); + avalon->device_fd = fd; + avalon->threads = AVALON_MINER_THREADS; + add_cgpu(avalon); + + ret = avalon_reset(fd, &ar); + if (ret) { + ; /* FIXME: I think IT IS avalon and wait on reset; + * avalon_close(fd); + * return false; */ + } + + avalon_infos = realloc(avalon_infos, + sizeof(struct avalon_info *) * + (total_devices + 1)); + + applog(LOG_INFO, "Avalon Detect: Found at %s, mark as %d", + devpath, avalon->device_id); + + avalon_infos[avalon->device_id] = (struct avalon_info *) + malloc(sizeof(struct avalon_info)); + if (unlikely(!(avalon_infos[avalon->device_id]))) + quit(1, "Failed to malloc avalon_infos"); + + info = avalon_infos[avalon->device_id]; + + memset(info, 0, sizeof(struct avalon_info)); + + info->baud = baud; + info->miner_count = miner_count; + info->asic_count = asic_count; + info->timeout = timeout; + + info->fan_pwm = AVALON_DEFAULT_FAN_MIN_PWM; + info->temp_max = 0; + /* This is for check the temp/fan every 3~4s */ + info->temp_history_count = (4 / (float)((float)info->timeout * ((float)1.67/0x32))) + 1; + if (info->temp_history_count <= 0) + info->temp_history_count = 1; + + info->temp_history_index = 0; + info->temp_sum = 0; + info->temp_old = 0; + info->frequency = frequency; + + /* Set asic to idle mode after detect */ + avalon_idle(avalon); + avalon->device_fd = -1; + + avalon_close(fd); + return true; +} + +static inline void avalon_detect() +{ + serial_detect(&avalon_drv, avalon_detect_one); +} + +static void __avalon_init(struct cgpu_info *avalon) +{ + applog(LOG_INFO, "Avalon: Opened on %s", avalon->device_path); +} + +static void avalon_init(struct cgpu_info *avalon) +{ + struct avalon_result ar; + int fd, ret; + + avalon->device_fd = -1; + fd = avalon_open(avalon->device_path, + avalon_infos[avalon->device_id]->baud); + if (unlikely(fd == -1)) { + applog(LOG_ERR, "Avalon: Failed to open on %s", + avalon->device_path); + return; + } + + ret = avalon_reset(fd, &ar); + if (ret) { + avalon_close(fd); + return; + } + + avalon->device_fd = fd; + __avalon_init(avalon); +} + +static bool avalon_prepare(struct thr_info *thr) +{ + struct cgpu_info *avalon = thr->cgpu; + struct avalon_info *info = avalon_infos[avalon->device_id]; + struct timeval now; + + free(avalon->works); + avalon->works = calloc(info->miner_count * sizeof(struct work *), + AVALON_ARRAY_SIZE); + if (!avalon->works) + quit(1, "Failed to calloc avalon works in avalon_prepare"); + if (avalon->device_fd == -1) + avalon_init(avalon); + else + __avalon_init(avalon); + + gettimeofday(&now, NULL); + get_datestamp(avalon->init, &now); + return true; +} + +static void avalon_free_work(struct thr_info *thr) +{ + struct cgpu_info *avalon; + struct avalon_info *info; + struct work **works; + int i; + + avalon = thr->cgpu; + avalon->queued = 0; + if (unlikely(!avalon->works)) + return; + works = avalon->works; + info = avalon_infos[avalon->device_id]; + + for (i = 0; i < info->miner_count * 4; i++) { + if (works[i]) { + work_completed(avalon, works[i]); + works[i] = NULL; + } + } +} + +static void do_avalon_close(struct thr_info *thr) +{ + struct avalon_result ar; + struct cgpu_info *avalon = thr->cgpu; + struct avalon_info *info = avalon_infos[avalon->device_id]; + + avalon_free_work(thr); + sleep(1); + avalon_reset(avalon->device_fd, &ar); + avalon_idle(avalon); + avalon_close(avalon->device_fd); + avalon->device_fd = -1; + + info->no_matching_work = 0; +} + +static inline void record_temp_fan(struct avalon_info *info, struct avalon_result *ar, float *temp_avg) +{ + info->fan0 = ar->fan0 * AVALON_FAN_FACTOR; + info->fan1 = ar->fan1 * AVALON_FAN_FACTOR; + info->fan2 = ar->fan2 * AVALON_FAN_FACTOR; + + info->temp0 = ar->temp0; + info->temp1 = ar->temp1; + info->temp2 = ar->temp2; + if (ar->temp0 & 0x80) { + ar->temp0 &= 0x7f; + info->temp0 = 0 - ((~ar->temp0 & 0x7f) + 1); + } + if (ar->temp1 & 0x80) { + ar->temp1 &= 0x7f; + info->temp1 = 0 - ((~ar->temp1 & 0x7f) + 1); + } + if (ar->temp2 & 0x80) { + ar->temp2 &= 0x7f; + info->temp2 = 0 - ((~ar->temp2 & 0x7f) + 1); + } + + *temp_avg = info->temp2 > info->temp1 ? info->temp2 : info->temp1; + + if (info->temp0 > info->temp_max) + info->temp_max = info->temp0; + if (info->temp1 > info->temp_max) + info->temp_max = info->temp1; + if (info->temp2 > info->temp_max) + info->temp_max = info->temp2; +} + +static inline void adjust_fan(struct avalon_info *info) +{ + int temp_new; + + temp_new = info->temp_sum / info->temp_history_count; + + if (temp_new < 35) { + info->fan_pwm = AVALON_DEFAULT_FAN_MIN_PWM; + info->temp_old = temp_new; + } else if (temp_new > 55) { + info->fan_pwm = AVALON_DEFAULT_FAN_MAX_PWM; + info->temp_old = temp_new; + } else if (abs(temp_new - info->temp_old) >= 2) { + info->fan_pwm = AVALON_DEFAULT_FAN_MIN_PWM + (temp_new - 35) * 6.4; + info->temp_old = temp_new; + } +} + +/* We use a replacement algorithm to only remove references to work done from + * the buffer when we need the extra space for new work. */ +static bool avalon_fill(struct cgpu_info *avalon) +{ + int subid, slot, mc = avalon_infos[avalon->device_id]->miner_count; + struct work *work; + + if (avalon->queued >= mc) + return true; + work = get_queued(avalon); + if (unlikely(!work)) + return false; + subid = avalon->queued++; + work->subid = subid; + slot = avalon->work_array * mc + subid; + if (likely(avalon->works[slot])) + work_completed(avalon, avalon->works[slot]); + avalon->works[slot] = work; + if (avalon->queued >= mc) + return true; + return false; +} + +static void avalon_rotate_array(struct cgpu_info *avalon) +{ + avalon->queued = 0; + if (++avalon->work_array >= AVALON_ARRAY_SIZE) + avalon->work_array = 0; +} + +static int64_t avalon_scanhash(struct thr_info *thr) +{ + struct cgpu_info *avalon; + struct work **works; + int fd, ret = AVA_GETS_OK, full; + + struct avalon_info *info; + struct avalon_task at; + struct avalon_result ar; + int i; + int avalon_get_work_count; + int start_count, end_count; + + struct timeval tv_start, tv_finish, elapsed; + uint32_t nonce; + int64_t hash_count; + static int first_try = 0; + int result_wrong; + + avalon = thr->cgpu; + works = avalon->works; + info = avalon_infos[avalon->device_id]; + avalon_get_work_count = info->miner_count; + + if (unlikely(avalon->device_fd == -1)) { + if (!avalon_prepare(thr)) { + applog(LOG_ERR, "AVA%i: Comms error(open)", + avalon->device_id); + dev_error(avalon, REASON_DEV_COMMS_ERROR); + /* fail the device if the reopen attempt fails */ + return -1; + } + } + fd = avalon->device_fd; +#ifndef WIN32 + tcflush(fd, TCOFLUSH); +#endif + + start_count = avalon->work_array * avalon_get_work_count; + end_count = start_count + avalon_get_work_count; + i = start_count; + while (true) { + avalon_init_task(&at, 0, 0, info->fan_pwm, + info->timeout, info->asic_count, + info->miner_count, 1, 0, info->frequency); + avalon_create_task(&at, works[i]); + ret = avalon_send_task(fd, &at, avalon); + if (unlikely(ret == AVA_SEND_ERROR || + (ret == AVA_SEND_BUFFER_EMPTY && + (i + 1 == end_count) && + first_try))) { + do_avalon_close(thr); + applog(LOG_ERR, "AVA%i: Comms error(buffer)", + avalon->device_id); + dev_error(avalon, REASON_DEV_COMMS_ERROR); + first_try = 0; + sleep(1); + avalon_init(avalon); + return 0; /* This should never happen */ + } + if (ret == AVA_SEND_BUFFER_EMPTY && (i + 1 == end_count)) { + first_try = 1; + avalon_rotate_array(avalon); + return 0xffffffff; + } + + works[i]->blk.nonce = 0xffffffff; + + if (ret == AVA_SEND_BUFFER_FULL) + break; + + i++; + } + if (unlikely(first_try)) + first_try = 0; + + elapsed.tv_sec = elapsed.tv_usec = 0; + gettimeofday(&tv_start, NULL); + + result_wrong = 0; + hash_count = 0; + while (true) { + full = avalon_buffer_full(fd); + applog(LOG_DEBUG, "Avalon: Buffer full: %s", + ((full == AVA_BUFFER_FULL) ? "Yes" : "No")); + if (unlikely(full == AVA_BUFFER_EMPTY)) + break; + + ret = avalon_get_result(fd, &ar, thr, &tv_finish); + if (unlikely(ret == AVA_GETS_ERROR)) { + do_avalon_close(thr); + applog(LOG_ERR, + "AVA%i: Comms error(read)", avalon->device_id); + dev_error(avalon, REASON_DEV_COMMS_ERROR); + return 0; + } + if (unlikely(ret == AVA_GETS_RESTART)) + break; + if (unlikely(ret == AVA_GETS_TIMEOUT)) { + timersub(&tv_finish, &tv_start, &elapsed); + applog(LOG_DEBUG, "Avalon: no nonce in (%ld.%06lds)", + elapsed.tv_sec, elapsed.tv_usec); + continue; + } + + if (!avalon_decode_nonce(thr, &ar, &nonce)) { + info->no_matching_work++; + result_wrong++; + + if (unlikely(result_wrong >= avalon_get_work_count)) + break; + + if (opt_debug) { + timersub(&tv_finish, &tv_start, &elapsed); + applog(LOG_DEBUG,"Avalon: no matching work: %d" + " (%ld.%06lds)", info->no_matching_work, + elapsed.tv_sec, elapsed.tv_usec); + } + continue; + } + + hash_count += 0xffffffff; + if (opt_debug) { + timersub(&tv_finish, &tv_start, &elapsed); + applog(LOG_DEBUG, + "Avalon: nonce = 0x%08x = 0x%08llx hashes " + "(%ld.%06lds)", nonce, hash_count, + elapsed.tv_sec, elapsed.tv_usec); + } + } + if (hash_count) { + if (avalon->results < AVALON_ARRAY_SIZE) + avalon->results++; + } else if (unlikely((result_wrong >= avalon_get_work_count ) || + (ret != AVA_GETS_RESTART && --avalon->results < 0))) { + /* Look for all invalid results, or consecutive failure + * to generate any results suggesting the FPGA + * controller has screwed up. */ + do_avalon_close(thr); + applog(LOG_ERR, + "AVA%i: FPGA controller messed up, %d wrong results", + avalon->device_id, result_wrong); + dev_error(avalon, REASON_DEV_COMMS_ERROR); + sleep(1); + avalon_init(avalon); + return 0; + } + + avalon_rotate_array(avalon); + + record_temp_fan(info, &ar, &(avalon->temp)); + applog(LOG_INFO, + "Avalon: Fan1: %d/m, Fan2: %d/m, Fan3: %d/m\t" + "Temp1: %dC, Temp2: %dC, Temp3: %dC, TempMAX: %dC", + info->fan0, info->fan1, info->fan2, + info->temp0, info->temp1, info->temp2, info->temp_max); + info->temp_history_index++; + info->temp_sum += avalon->temp; + applog(LOG_DEBUG, "Avalon: temp_index: %d, temp_count: %d, temp_old: %d", + info->temp_history_index, info->temp_history_count, info->temp_old); + if (info->temp_history_index == info->temp_history_count) { + adjust_fan(info); + info->temp_history_index = 0; + info->temp_sum = 0; + } + + /* This hashmeter is just a utility counter based on returned shares */ + return hash_count; +} + +static struct api_data *avalon_api_stats(struct cgpu_info *cgpu) +{ + struct api_data *root = NULL; + struct avalon_info *info = avalon_infos[cgpu->device_id]; + + root = api_add_int(root, "baud", &(info->baud), false); + root = api_add_int(root, "miner_count", &(info->miner_count),false); + root = api_add_int(root, "asic_count", &(info->asic_count), false); + root = api_add_int(root, "timeout", &(info->timeout), false); + root = api_add_int(root, "frequency", &(info->frequency), false); + + root = api_add_int(root, "fan1", &(info->fan0), false); + root = api_add_int(root, "fan2", &(info->fan1), false); + root = api_add_int(root, "fan3", &(info->fan2), false); + + root = api_add_int(root, "temp1", &(info->temp0), false); + root = api_add_int(root, "temp2", &(info->temp1), false); + root = api_add_int(root, "temp3", &(info->temp2), false); + root = api_add_int(root, "temp_max", &(info->temp_max), false); + + root = api_add_int(root, "no_matching_work", &(info->no_matching_work), false); + root = api_add_int(root, "matching_work_count1", &(info->matching_work[0]), false); + root = api_add_int(root, "matching_work_count2", &(info->matching_work[1]), false); + root = api_add_int(root, "matching_work_count3", &(info->matching_work[2]), false); + root = api_add_int(root, "matching_work_count4", &(info->matching_work[3]), false); + root = api_add_int(root, "matching_work_count5", &(info->matching_work[4]), false); + root = api_add_int(root, "matching_work_count6", &(info->matching_work[5]), false); + root = api_add_int(root, "matching_work_count7", &(info->matching_work[6]), false); + root = api_add_int(root, "matching_work_count8", &(info->matching_work[7]), false); + root = api_add_int(root, "matching_work_count9", &(info->matching_work[8]), false); + root = api_add_int(root, "matching_work_count10", &(info->matching_work[9]), false); + root = api_add_int(root, "matching_work_count11", &(info->matching_work[10]), false); + root = api_add_int(root, "matching_work_count12", &(info->matching_work[11]), false); + root = api_add_int(root, "matching_work_count13", &(info->matching_work[12]), false); + root = api_add_int(root, "matching_work_count14", &(info->matching_work[13]), false); + root = api_add_int(root, "matching_work_count15", &(info->matching_work[14]), false); + root = api_add_int(root, "matching_work_count16", &(info->matching_work[15]), false); + root = api_add_int(root, "matching_work_count17", &(info->matching_work[16]), false); + root = api_add_int(root, "matching_work_count18", &(info->matching_work[17]), false); + root = api_add_int(root, "matching_work_count19", &(info->matching_work[18]), false); + root = api_add_int(root, "matching_work_count20", &(info->matching_work[19]), false); + root = api_add_int(root, "matching_work_count21", &(info->matching_work[20]), false); + root = api_add_int(root, "matching_work_count22", &(info->matching_work[21]), false); + root = api_add_int(root, "matching_work_count23", &(info->matching_work[22]), false); + root = api_add_int(root, "matching_work_count24", &(info->matching_work[23]), false); + + return root; +} + +static void avalon_shutdown(struct thr_info *thr) +{ + do_avalon_close(thr); +} + +struct device_drv avalon_drv = { + .drv_id = DRIVER_AVALON, + .dname = "avalon", + .name = "AVA", + .drv_detect = avalon_detect, + .thread_prepare = avalon_prepare, + .hash_work = hash_queued_work, + .queue_full = avalon_fill, + .scanwork = avalon_scanhash, + .get_api_stats = avalon_api_stats, + .reinit_device = avalon_init, + .thread_shutdown = avalon_shutdown, +}; diff --git a/driver-avalon.h b/driver-avalon.h new file mode 100644 index 00000000..ed745e77 --- /dev/null +++ b/driver-avalon.h @@ -0,0 +1,130 @@ +/* + * Copyright 2013 Avalon project + * + * 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 + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#ifndef AVALON_H +#define AVALON_H + +#ifdef USE_AVALON + +#define AVALON_RESET_FAULT_DECISECONDS 1 +#define AVALON_MINER_THREADS 1 + +#define AVALON_IO_SPEED 115200 +#define AVALON_HASH_TIME_FACTOR ((float)1.67/0x32) +#define AVALON_RESET_PITCH (300*1000*1000) + +#define AVALON_FAN_FACTOR 120 +#define AVALON_DEFAULT_FAN_MAX_PWM 0xA0 /* 100% */ +#define AVALON_DEFAULT_FAN_MIN_PWM 0x20 /* 20% */ + +#define AVALON_DEFAULT_TIMEOUT 0x32 +#define AVALON_DEFAULT_FREQUENCY 256 +#define AVALON_DEFAULT_MINER_NUM 0x20 +#define AVALON_DEFAULT_ASIC_NUM 0xA + +struct avalon_task { + uint8_t reset :1; + uint8_t flush_fifo :1; + uint8_t fan_eft :1; + uint8_t timer_eft :1; + uint8_t asic_num :4; + uint8_t fan_pwm_data; + uint8_t timeout_data; + uint8_t miner_num; + + uint8_t nonce_elf :1; + uint8_t gate_miner_elf :1; + uint8_t asic_pll :1; + uint8_t gate_miner :1; + uint8_t _pad0 :4; + uint8_t _pad1[3]; + uint32_t _pad2; + + uint8_t midstate[32]; + uint8_t data[12]; +} __attribute__((packed, aligned(4))); + +struct avalon_result { + uint32_t nonce; + uint8_t data[12]; + uint8_t midstate[32]; + + uint8_t fan0; + uint8_t fan1; + uint8_t fan2; + uint8_t temp0; + uint8_t temp1; + uint8_t temp2; + uint8_t _pad0[2]; + + uint16_t fifo_wp; + uint16_t fifo_rp; + uint8_t chip_num; + uint8_t pwm_data; + uint8_t timeout; + uint8_t miner_num; +} __attribute__((packed, aligned(4))); + +struct avalon_info { + int baud; + int miner_count; + int asic_count; + int timeout; + + int fan0; + int fan1; + int fan2; + + int temp0; + int temp1; + int temp2; + int temp_max; + int temp_history_count; + int temp_history_index; + int temp_sum; + int temp_old; + int fan_pwm; + + int no_matching_work; + int matching_work[AVALON_DEFAULT_MINER_NUM]; + + int frequency; +}; + +#define AVALON_WRITE_SIZE (sizeof(struct avalon_task)) +#define AVALON_READ_SIZE (sizeof(struct avalon_result)) +#define AVALON_ARRAY_SIZE 4 + +#define AVA_GETS_ERROR -1 +#define AVA_GETS_OK 0 +#define AVA_GETS_RESTART 1 +#define AVA_GETS_TIMEOUT 2 + +#define AVA_SEND_ERROR -1 +#define AVA_SEND_OK 0 +#define AVA_SEND_BUFFER_EMPTY 1 +#define AVA_SEND_BUFFER_FULL 2 + +#define AVA_BUFFER_FULL 0 +#define AVA_BUFFER_EMPTY 1 + +#define avalon_open2(devpath, baud, purge) serial_open(devpath, baud, AVALON_RESET_FAULT_DECISECONDS, purge) +#define avalon_open(devpath, baud) avalon_open2(devpath, baud, true) +#define avalon_close(fd) close(fd) + +#define avalon_buffer_full(fd) get_serial_cts(fd) + +#define AVALON_READ_TIME(baud) ((double)AVALON_READ_SIZE * (double)8.0 / (double)(baud)) +#define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1] +ASSERT1(sizeof(uint32_t) == 4); + +extern struct avalon_info **avalon_info; + +#endif /* USE_AVALON */ +#endif /* AVALON_H */ diff --git a/fpgautils.c b/fpgautils.c index 489c89cf..61706a57 100644 --- a/fpgautils.c +++ b/fpgautils.c @@ -1,4 +1,5 @@ /* + * Copyright 2013 Con Kolivas * Copyright 2012 Luke Dashjr * Copyright 2012 Andrew Smith * @@ -19,6 +20,7 @@ #ifndef WIN32 #include #include +#include #include #include #include @@ -32,10 +34,12 @@ #ifdef HAVE_LIBUDEV #include +#include #endif #include "elist.h" #include "logging.h" +#include "miner.h" #include "fpgautils.h" #ifdef HAVE_LIBUDEV @@ -382,6 +386,14 @@ int serial_open(const char *devpath, unsigned long baud, signed short timeout, b switch (baud) { case 0: break; + case 19200: + cfsetispeed(&my_termios, B19200); + cfsetospeed(&my_termios, B19200); + break; + case 38400: + cfsetispeed(&my_termios, B38400); + cfsetospeed(&my_termios, B38400); + break; case 57600: cfsetispeed(&my_termios, B57600); cfsetospeed(&my_termios, B57600); @@ -570,4 +582,14 @@ size_t _select_write(int fd, char *buf, size_t siz, struct timeval *timeout) return wrote; } +int get_serial_cts(int fd) +{ + int flags; + + if (!fd) + return -1; + + ioctl(fd, TIOCMGET, &flags); + return (flags & TIOCM_CTS) ? 1 : 0; +} #endif // ! WIN32 diff --git a/fpgautils.h b/fpgautils.h index b979b6c6..8a7dd834 100644 --- a/fpgautils.h +++ b/fpgautils.h @@ -36,6 +36,8 @@ extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol); extern FILE *open_bitstream(const char *dname, const char *filename); +extern int get_serial_cts(int fd); + #ifndef WIN32 extern const struct timeval tv_timeout_default; extern const struct timeval tv_inter_char_default; diff --git a/hexdump.c b/hexdump.c new file mode 100644 index 00000000..8951dde8 --- /dev/null +++ b/hexdump.c @@ -0,0 +1,77 @@ +/* + * hexdump implementation without depenecies to *printf() + * output is equal to 'hexdump -C' + * should be compatible to 64bit architectures + * + * Copyright (c) 2009 Daniel Mack + * + * 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 Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define hex_print(p) applog(LOG_DEBUG, "%s", p) + +static char nibble[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + +#define BYTES_PER_LINE 0x10 + +void hexdump(const uint8_t *p, unsigned int len) +{ + unsigned int i, addr; + unsigned int wordlen = sizeof(void*); + unsigned char v, line[BYTES_PER_LINE * 5]; + + for (addr = 0; addr < len; addr += BYTES_PER_LINE) { + /* clear line */ + for (i = 0; i < sizeof(line); i++) { + if (i == wordlen * 2 + 52 || + i == wordlen * 2 + 69) { + line[i] = '|'; + continue; + } + + if (i == wordlen * 2 + 70) { + line[i] = '\0'; + continue; + } + + line[i] = ' '; + } + + /* print address */ + for (i = 0; i < wordlen * 2; i++) { + v = addr >> ((wordlen * 2 - i - 1) * 4); + line[i] = nibble[v & 0xf]; + } + + /* dump content */ + for (i = 0; i < BYTES_PER_LINE; i++) { + int pos = (wordlen * 2) + 3 + (i / 8); + + if (addr + i >= len) + break; + + v = p[addr + i]; + line[pos + (i * 3) + 0] = nibble[v >> 4]; + line[pos + (i * 3) + 1] = nibble[v & 0xf]; + + /* character printable? */ + line[(wordlen * 2) + 53 + i] = + (v >= ' ' && v <= '~') ? v : '.'; + } + + hex_print(line); + } +} diff --git a/miner.h b/miner.h index 5ab227f5..3d275daf 100644 --- a/miner.h +++ b/miner.h @@ -120,9 +120,11 @@ static inline int fsync (int fd) #if (!defined(WIN32) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) \ || (defined(WIN32) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) -#define bswap_16 __builtin_bswap16 -#define bswap_32 __builtin_bswap32 -#define bswap_64 __builtin_bswap64 +#ifndef bswap_16 + #define bswap_16 __builtin_bswap16 + #define bswap_32 __builtin_bswap32 + #define bswap_64 __builtin_bswap64 +#endif #else #if HAVE_BYTESWAP_H #include @@ -421,10 +423,16 @@ struct cgpu_info { #ifdef USE_USBUTILS struct cg_usb_device *usbdev; #endif -#ifdef USE_ICARUS +#if defined(USE_ICARUS) || defined(USE_AVALON) int device_fd; #endif }; +#ifdef USE_AVALON + struct work **works; + int work_array; + int queued; + int results; +#endif #ifdef USE_USBUTILS struct cg_usb_info usbinfo; #endif @@ -787,6 +795,9 @@ extern bool opt_restart; extern char *opt_icarus_options; extern char *opt_icarus_timing; extern bool opt_worktime; +#ifdef USE_AVALON +extern char *opt_avalon_options; +#endif #ifdef USE_USBUTILS extern char *opt_usb_select; extern int opt_usbdump; @@ -795,6 +806,7 @@ extern bool opt_usb_list_all; #ifdef USE_BITFORCE extern bool opt_bfl_noncerange; #endif +extern bool ping; extern int swork_id; extern pthread_rwlock_t netacc_lock; diff --git a/usbutils.c b/usbutils.c index 69bd72ec..6b214c82 100644 --- a/usbutils.c +++ b/usbutils.c @@ -294,6 +294,8 @@ static const char *C_REQUESTQUEJOB_S = "RequestQueJob"; static const char *C_REQUESTQUEJOBSTATUS_S = "RequestQueJobStatus"; static const char *C_QUEJOB_S = "QueJob"; static const char *C_QUEJOBSTATUS_S = "QueJobStatus"; +static const char *C_QUEFLUSH_S = "QueFlush"; +static const char *C_QUEFLUSHREPLY_S = "QueFlushReply"; #ifdef EOL #undef EOL @@ -759,6 +761,8 @@ static void cgusb_check_init() usb_commands[C_REQUESTQUEJOBSTATUS] = C_REQUESTQUEJOBSTATUS_S; usb_commands[C_QUEJOB] = C_QUEJOB_S; usb_commands[C_QUEJOBSTATUS] = C_QUEJOBSTATUS_S; + usb_commands[C_QUEFLUSH] = C_QUEFLUSH_S; + usb_commands[C_QUEFLUSHREPLY] = C_QUEFLUSHREPLY_S; stats_initialised = true; } diff --git a/usbutils.h b/usbutils.h index 8e34d817..db279ce0 100644 --- a/usbutils.h +++ b/usbutils.h @@ -131,6 +131,8 @@ enum usb_cmds { C_REQUESTQUEJOBSTATUS, C_QUEJOB, C_QUEJOBSTATUS, + C_QUEFLUSH, + C_QUEFLUSHREPLY, C_MAX }; diff --git a/util.c b/util.c index b3cb9c03..ffe4b941 100644 --- a/util.c +++ b/util.c @@ -710,9 +710,7 @@ void tq_free(struct thread_q *tq) static void tq_freezethaw(struct thread_q *tq, bool frozen) { mutex_lock(&tq->mutex); - tq->frozen = frozen; - pthread_cond_signal(&tq->cond); mutex_unlock(&tq->mutex); } @@ -740,14 +738,12 @@ bool tq_push(struct thread_q *tq, void *data) INIT_LIST_HEAD(&ent->q_node); mutex_lock(&tq->mutex); - if (!tq->frozen) { list_add_tail(&ent->q_node, &tq->q); } else { free(ent); rc = false; } - pthread_cond_signal(&tq->cond); mutex_unlock(&tq->mutex); @@ -761,7 +757,6 @@ void *tq_pop(struct thread_q *tq, const struct timespec *abstime) int rc; mutex_lock(&tq->mutex); - if (!list_empty(&tq->q)) goto pop; @@ -773,16 +768,15 @@ void *tq_pop(struct thread_q *tq, const struct timespec *abstime) goto out; if (list_empty(&tq->q)) goto out; - pop: ent = list_entry(tq->q.next, struct tq_ent, q_node); rval = ent->data; list_del(&ent->q_node); free(ent); - out: mutex_unlock(&tq->mutex); + return rval; } @@ -898,16 +892,20 @@ bool extract_sockaddr(struct pool *pool, char *url) return true; } +enum send_ret { + SEND_OK, + SEND_SELECTFAIL, + SEND_SENDFAIL, + SEND_INACTIVE +}; + /* Send a single command across a socket, appending \n to it. This should all * be done under stratum lock except when first establishing the socket */ -static bool __stratum_send(struct pool *pool, char *s, ssize_t len) +static enum send_ret __stratum_send(struct pool *pool, char *s, ssize_t len) { SOCKETTYPE sock = pool->sock; ssize_t ssent = 0; - if (opt_protocol) - applog(LOG_DEBUG, "SEND: %s", s); - strcat(s, "\n"); len++; @@ -918,16 +916,12 @@ static bool __stratum_send(struct pool *pool, char *s, ssize_t len) FD_ZERO(&wd); FD_SET(sock, &wd); - if (select(sock + 1, NULL, &wd, NULL, &timeout) < 1) { - applog(LOG_DEBUG, "Write select failed on pool %d sock", pool->pool_no); - return false; - } + if (select(sock + 1, NULL, &wd, NULL, &timeout) < 1) + return SEND_SELECTFAIL; sent = send(pool->sock, s + ssent, len, 0); if (sent < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - applog(LOG_DEBUG, "Failed to curl_easy_send in stratum_send"); - return false; - } + if (errno != EAGAIN && errno != EWOULDBLOCK) + return SEND_SENDFAIL; sent = 0; } ssent += sent; @@ -937,21 +931,37 @@ 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; + return SEND_OK; } bool stratum_send(struct pool *pool, char *s, ssize_t len) { - bool ret = false; + enum send_ret ret = SEND_INACTIVE; + + if (opt_protocol) + applog(LOG_DEBUG, "SEND: %s", s); mutex_lock(&pool->stratum_lock); if (pool->stratum_active) ret = __stratum_send(pool, s, len); - else - applog(LOG_DEBUG, "Stratum send failed due to no pool stratum_active"); mutex_unlock(&pool->stratum_lock); - return ret; + /* This is to avoid doing applog under stratum_lock */ + switch (ret) { + default: + case SEND_OK: + break; + case SEND_SELECTFAIL: + applog(LOG_DEBUG, "Write select failed on pool %d sock", pool->pool_no); + break; + case SEND_SENDFAIL: + applog(LOG_DEBUG, "Failed to curl_easy_send in stratum_send"); + break; + case SEND_INACTIVE: + applog(LOG_DEBUG, "Stratum send failed due to no pool stratum_active"); + break; + } + return (ret == SEND_OK); } static bool socket_full(struct pool *pool, bool wait) @@ -1011,7 +1021,8 @@ static void recalloc_sock(struct pool *pool, size_t len) if (new < pool->sockbuf_size) return; new = new + (RBUFSIZE - (new % RBUFSIZE)); - applog(LOG_DEBUG, "Recallocing pool sockbuf to %d", new); + // Avoid potentially recursive locking + // applog(LOG_DEBUG, "Recallocing pool sockbuf to %d", new); pool->sockbuf = realloc(pool->sockbuf, new); if (!pool->sockbuf) quit(1, "Failed to realloc pool sockbuf in recalloc_sock"); @@ -1019,6 +1030,12 @@ static void recalloc_sock(struct pool *pool, size_t len) pool->sockbuf_size = new; } +enum recv_ret { + RECV_OK, + RECV_CLOSED, + RECV_RECVFAIL +}; + /* Peeks at a socket to find the first end of line and then reads just that * from the socket and returns that as a malloced char */ char *recv_line(struct pool *pool) @@ -1027,6 +1044,7 @@ char *recv_line(struct pool *pool) char *tok, *sret = NULL; if (!strstr(pool->sockbuf, "\n")) { + enum recv_ret ret = RECV_OK; struct timeval rstart, now; gettimeofday(&rstart, NULL); @@ -1044,11 +1062,11 @@ char *recv_line(struct pool *pool) memset(s, 0, RBUFSIZE); n = recv(pool->sock, s, RECVSIZE, 0); if (!n) { - applog(LOG_DEBUG, "Socket closed waiting in recv_line"); + ret = RECV_CLOSED; break; } if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - applog(LOG_DEBUG, "Failed to recv sock in recv_line"); + ret = RECV_RECVFAIL; break; } slen = strlen(s); @@ -1057,6 +1075,18 @@ char *recv_line(struct pool *pool) gettimeofday(&now, NULL); } while (tdiff(&now, &rstart) < 60 && !strstr(pool->sockbuf, "\n")); mutex_unlock(&pool->stratum_lock); + + switch (ret) { + default: + case RECV_OK: + break; + case RECV_CLOSED: + applog(LOG_DEBUG, "Socket closed waiting in recv_line"); + break; + case RECV_RECVFAIL: + applog(LOG_DEBUG, "Failed to recv sock in recv_line"); + break; + } } buflen = strlen(pool->sockbuf); @@ -1441,6 +1471,7 @@ static bool setup_stratum_curl(struct pool *pool) if (unlikely(!pool->stratum_curl)) quit(1, "Failed to curl_easy_init in initiate_stratum"); mutex_unlock(&pool->stratum_lock); + curl = pool->stratum_curl; if (!pool->sockbuf) { @@ -1470,6 +1501,8 @@ static bool setup_stratum_curl(struct pool *pool) curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1); if (curl_easy_perform(curl)) { applog(LOG_INFO, "Stratum connect failed to pool %d: %s", pool->pool_no, curl_err_str); + curl_easy_cleanup(curl); + pool->stratum_curl = NULL; return false; } curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, (long *)&pool->sock); @@ -1517,6 +1550,7 @@ void suspend_stratum(struct pool *pool) { clear_sockbuf(pool); applog(LOG_INFO, "Closing socket for stratum pool %d", pool->pool_no); + mutex_lock(&pool->stratum_lock); pool->stratum_active = pool->stratum_notify = false; if (pool->stratum_curl) { @@ -1561,7 +1595,7 @@ resend: sprintf(s, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": [\""PACKAGE"/"VERSION"\"]}", swork_id++); } - if (!__stratum_send(pool, s, strlen(s))) { + if (__stratum_send(pool, s, strlen(s)) != SEND_OK) { applog(LOG_DEBUG, "Failed to send s in initiate_stratum"); goto out; } @@ -1654,6 +1688,7 @@ out: free(pool->nonce1); pool->sessionid = pool->nonce1 = NULL; cg_wunlock(&pool->data_lock); + applog(LOG_DEBUG, "Failed to resume stratum, trying afresh"); noresume = true; goto resend;