diff --git a/Makefile.am b/Makefile.am index 42bf4fde4c..6b08afb65b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -169,6 +169,10 @@ if HAS_AVALON8 cgminer_SOURCES += driver-avalon8.c driver-avalon8.h endif +if HAS_AVALON9 +cgminer_SOURCES += driver-avalon9.c driver-avalon9.h +endif + if NEED_I2C_CONTEXT cgminer_SOURCES += i2c-context.c endif diff --git a/api.c b/api.c index c58dfdf7f4..cbce52fc1b 100644 --- a/api.c +++ b/api.c @@ -32,7 +32,7 @@ defined(USE_MINION) || defined(USE_COINTERRA) || defined(USE_BITMINE_A1) || \ defined(USE_ANT_S1) || defined(USE_ANT_S2) || defined(USE_ANT_S3) || defined(USE_SP10) || \ defined(USE_SP30) || defined(USE_ICARUS) || defined(USE_HASHRATIO) || defined(USE_AVALON_MINER) || \ - defined(USE_AVALON7) || defined(USE_AVALON8) + defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_AVALON9) #define HAVE_AN_ASIC 1 #endif diff --git a/cgminer.c b/cgminer.c index 185eabbb12..041e552a04 100644 --- a/cgminer.c +++ b/cgminer.c @@ -91,6 +91,10 @@ char *curly = ":D"; #include "driver-avalon8.h" #endif +#ifdef USE_AVALON9 +#include "driver-avalon9.h" +#endif + #ifdef USE_AVALON_MINER #include "driver-avalon-miner.h" #endif @@ -279,6 +283,12 @@ static char *opt_set_avalon8_voltage_level; static char *opt_set_avalon8_voltage_level_offset; static char *opt_set_avalon8_freq; #endif +#ifdef USE_AVALON9 +static char *opt_set_avalon9_fan; +static char *opt_set_avalon9_voltage_level; +static char *opt_set_avalon9_voltage_level_offset; +static char *opt_set_avalon9_freq; +#endif #ifdef USE_AVALON_MINER static char *opt_set_avalonm_voltage; static char *opt_set_avalonm_freq; @@ -845,7 +855,7 @@ static char *set_int_1_to_65535(const char *arg, int *i) return set_int_range(arg, i, 1, 65535); } -#ifdef USE_AVALON8 +#if defined(USE_AVALON8) || defined(USE_AVALON9) static char *set_int_0_to_1(const char *arg, int *i) { return set_int_range(arg, i, 0, 1); @@ -1629,6 +1639,122 @@ static struct opt_table opt_config_table[] = { set_int_0_to_7, opt_show_intval, &opt_avalon8_spdhigh, "Set Avalon8 spdhigh, range 0-7."), #endif +#ifdef USE_AVALON9 + OPT_WITH_CBARG("--avalon9-voltage-level", + set_avalon9_voltage_level, NULL, &opt_set_avalon9_voltage_level, + "Set Avalon9 default level of core voltage, range:[0, 15], step: 1"), + OPT_WITH_CBARG("--avalon9-voltage-level-offset", + set_avalon9_voltage_level_offset, NULL, &opt_set_avalon9_voltage_level_offset, + "Set Avalon9 default offset of core voltage level, range:[-2, 1], step: 1"), + OPT_WITH_CBARG("--avalon9-freq", + set_avalon9_freq, NULL, &opt_set_avalon9_freq, + "Set Avalon9 default frequency, range:[25, 1200], step: 25, example: 800"), + OPT_WITH_ARG("--avalon9-freq-sel", + set_int_0_to_7, opt_show_intval, &opt_avalon9_freq_sel, + "Set Avalon9 default frequency select, range:[0, 7], step: 1, example: 7"), + OPT_WITH_CBARG("--avalon9-fan", + set_avalon9_fan, NULL, &opt_set_avalon9_fan, + "Set Avalon9 target fan speed, range:[0, 100], step: 1, example: 0-100"), + OPT_WITH_ARG("--avalon9-temp", + set_int_0_to_100, opt_show_intval, &opt_avalon9_temp_target, + "Set Avalon9 target temperature, range:[0, 100]"), + OPT_WITH_ARG("--avalon9-polling-delay", + set_int_1_to_65535, opt_show_intval, &opt_avalon9_polling_delay, + "Set Avalon9 polling delay value (ms)"), + OPT_WITH_ARG("--avalon9-aucspeed", + opt_set_intval, opt_show_intval, &opt_avalon9_aucspeed, + "Set AUC3 IIC bus speed"), + OPT_WITH_ARG("--avalon9-aucxdelay", + opt_set_intval, opt_show_intval, &opt_avalon9_aucxdelay, + "Set AUC3 IIC xfer read delay, 4800 ~= 1ms"), + OPT_WITH_ARG("--avalon9-smart-speed", + opt_set_intval, opt_show_intval, &opt_avalon9_smart_speed, + "Set Avalon9 smart speed, range 0-1. 0 means Disable"), + OPT_WITH_ARG("--avalon9-th-pass", + set_int_0_to_65535, opt_show_intval, &opt_avalon9_th_pass, + "Set A3206 th pass value"), + OPT_WITH_ARG("--avalon9-th-fail", + set_int_0_to_65535, opt_show_intval, &opt_avalon9_th_fail, + "Set A3206 th fail value"), + OPT_WITH_ARG("--avalon9-th-init", + set_int_0_to_65535, opt_show_intval, &opt_avalon9_th_init, + "Set A3206 th init value"), + OPT_WITH_ARG("--avalon9-th-ms", + set_int_0_to_32767, opt_show_intval, &opt_avalon9_th_ms, + "Set A3206 th ms value"), + OPT_WITH_ARG("--avalon9-th-timeout", + opt_set_uintval, opt_show_uintval, &opt_avalon9_th_timeout, + "Set A3206 th timeout value"), + OPT_WITH_ARG("--avalon9-th-add", + set_int_0_to_1, opt_show_intval, &opt_avalon9_th_add, + "Set A3206 th add value"), + OPT_WITH_ARG("--avalon9-th-mssel", + set_int_0_to_1, opt_show_intval, &opt_avalon9_th_mssel, + "Set A3206 th mssel value"), + OPT_WITH_ARG("--avalon9-lv2-th-add", + set_int_0_to_1, opt_show_intval, &opt_avalon9_lv2_th_add, + "Set A3206 lv2 th add value"), + OPT_WITH_ARG("--avalon9-lv2-th-ms", + set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv2_th_ms, + "Set A3206 lv2 th ms value"), + OPT_WITH_ARG("--avalon9-lv3-th-add", + set_int_0_to_1, opt_show_intval, &opt_avalon9_lv3_th_add, + "Set A3206 lv3 th add value"), + OPT_WITH_ARG("--avalon9-lv3-th-ms", + set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv3_th_ms, + "Set A3206 lv3 th ms value"), + OPT_WITH_ARG("--avalon9-lv4-th-add", + set_int_0_to_1, opt_show_intval, &opt_avalon9_lv4_th_add, + "Set A3206 lv4 th add value"), + OPT_WITH_ARG("--avalon9-lv4-th-ms", + set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv4_th_ms, + "Set A3206 lv4 th ms value"), + OPT_WITH_ARG("--avalon9-lv5-th-add", + set_int_0_to_1, opt_show_intval, &opt_avalon9_lv5_th_add, + "Set A3206 lv5 th add value"), + OPT_WITH_ARG("--avalon9-lv5-th-ms", + set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv5_th_ms, + "Set A3206 lv5 th ms value"), + OPT_WITH_ARG("--avalon9-lv6-th-add", + set_int_0_to_1, opt_show_intval, &opt_avalon9_lv6_th_add, + "Set A3206 lv6 th add value"), + OPT_WITH_ARG("--avalon9-lv6-th-ms", + set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv6_th_ms, + "Set A3206 lv6 th ms value"), + OPT_WITH_ARG("--avalon9-lv7-th-add", + set_int_0_to_1, opt_show_intval, &opt_avalon9_lv7_th_add, + "Set A3206 lv7 th add value"), + OPT_WITH_ARG("--avalon9-lv7-th-ms", + set_int_0_to_32767, opt_show_intval, &opt_avalon9_lv7_th_ms, + "Set A3206 lv7 th ms value"), + OPT_WITHOUT_ARG("--avalon9-iic-detect", + opt_set_bool, &opt_avalon9_iic_detect, + "Enable Avalon9 detect through iic controller"), + OPT_WITH_ARG("--avalon9-nonce-mask", + set_int_24_to_32, opt_show_intval, &opt_avalon9_nonce_mask, + "Set A3206 nonce mask, range 24-32."), + OPT_WITH_ARG("--avalon9-nonce-check", + set_int_0_to_1, opt_show_intval, &opt_avalon9_nonce_check, + "Set A3206 nonce check, range 0-1."), + OPT_WITH_ARG("--avalon9-roll-enable", + set_int_0_to_1, opt_show_intval, &opt_avalon9_roll_enable, + "Set A3206 roll enable, range 0-1."), + OPT_WITH_ARG("--avalon9-mux-l2h", + set_int_0_to_2, opt_show_intval, &opt_avalon9_mux_l2h, + "Set Avalon9 mux l2h, range 0-2."), + OPT_WITH_ARG("--avalon9-mux-h2l", + set_int_0_to_1, opt_show_intval, &opt_avalon9_mux_h2l, + "Set Avalon9 mux h2l, range 0-1."), + OPT_WITH_ARG("--avalon9-h2ltime0-spd", + set_int_0_to_255, opt_show_intval, &opt_avalon9_h2ltime0_spd, + "Set Avalon9 h2ltime0 spd, range 0-255."), + OPT_WITH_ARG("--avalon9-spdlow", + set_int_0_to_7, opt_show_intval, &opt_avalon9_spdlow, + "Set Avalon9 spdlow, range 0-7."), + OPT_WITH_ARG("--avalon9-spdhigh", + set_int_0_to_7, opt_show_intval, &opt_avalon9_spdhigh, + "Set Avalon9 spdhigh, range 0-7."), +#endif #ifdef USE_AVALON_MINER OPT_WITH_CBARG("--avalonm-voltage", set_avalonm_voltage, NULL, &opt_set_avalonm_voltage, @@ -2335,6 +2461,9 @@ static char *opt_verusage_and_exit(const char *extra) #ifdef USE_AVALON8 "avalon8 " #endif +#ifdef USE_AVALON9 + "avalon9 " +#endif #ifdef USE_AVALON_MINER "avalon miner" #endif @@ -7440,7 +7569,7 @@ void set_target(unsigned char *dest_target, double diff) cg_memcpy(dest_target, target, 32); } -#if defined (USE_AVALON2) || defined (USE_AVALON4) || defined (USE_AVALON7) || defined (USE_AVALON8) || defined (USE_AVALON_MINER) || defined (USE_HASHRATIO) +#if defined (USE_AVALON2) || defined (USE_AVALON4) || defined (USE_AVALON7) || defined (USE_AVALON8) || defined (USE_AVALON9) || defined (USE_AVALON_MINER) || defined (USE_HASHRATIO) bool submit_nonce2_nonce(struct thr_info *thr, struct pool *pool, struct pool *real_pool, uint32_t nonce2, uint32_t nonce, uint32_t ntime) { diff --git a/configure.ac b/configure.ac index ae1624022f..1c4e795ba8 100644 --- a/configure.ac +++ b/configure.ac @@ -230,6 +230,17 @@ if test "x$avalon8" = xyes; then fi AM_CONDITIONAL([HAS_AVALON8], [test x$avalon8 = xyes]) +avalon9="no" + +AC_ARG_ENABLE([avalon9], + [AC_HELP_STRING([--enable-avalon9],[Compile support for Avalon9 (default disabled)])], + [avalon9=$enableval] + ) +if test "x$avalon9" = xyes; then + AC_DEFINE([USE_AVALON9], [1], [Defined to 1 if Avalon9 support is wanted]) +fi +AM_CONDITIONAL([HAS_AVALON9], [test x$avalon9 = xyes]) + avalon_miner="no" AC_ARG_ENABLE([avalon_miner], @@ -517,7 +528,7 @@ fi #Add a new device to this list if it needs libusb, along with a no on the end. -if test x$avalon$avalon2$avalon4$avalon7$avalon8$avalon_miner$bitforce$bitfury$blockerupter$modminer$bflsc$icarus$hashfast$hashratio$klondike$drillbit$cointerra$ants1$ants3 != xnonononononononononononononononononono; then +if test x$avalon$avalon2$avalon4$avalon7$avalon8$avalon9$avalon_miner$bitforce$bitfury$blockerupter$modminer$bflsc$icarus$hashfast$hashratio$klondike$drillbit$cointerra$ants1$ants3 != xnononononononononononononononononononono; then want_usbutils=true else want_usbutils=false @@ -529,7 +540,7 @@ else want_libbitfury=false fi -if test x$avalon2$avalon4$avalon7$avalon8$avalon_miner$hashratio != xnononononono; then +if test x$avalon2$avalon4$avalon7$avalon8$avalon9$avalon_miner$hashratio != xnonononononono; then want_crc16=true else want_crc16=false @@ -542,7 +553,7 @@ AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes]) AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue]) AM_CONDITIONAL([HAVE_x86_64], [test x$have_x86_64 = xtrue]) AM_CONDITIONAL([WANT_CRC16], [test x$want_crc16 != xfalse]) -AM_CONDITIONAL([NEED_I2C_CONTEXT], [test x$avalon4$avalon7$avalon8 != xnonono]) +AM_CONDITIONAL([NEED_I2C_CONTEXT], [test x$avalon4$avalon7$avalon8$avalon9 != xnononono]) AM_CONDITIONAL([SUPPORT_SSP], [test x$avalon7 != xno]) if test "x$want_usbutils" != xfalse; then @@ -781,6 +792,12 @@ else echo " Avalon8.ASICs........: Disabled" fi +if test "x$avalon9" = xyes; then + echo " Avalon9.ASICs........: Enabled" +else + echo " Avalon9.ASICs........: Disabled" +fi + if test "x$avalon_miner" = xyes; then echo " Avalon miner.ASICs...: Enabled" else @@ -898,7 +915,7 @@ else fi #Add any new device to this, along with a no on the end of the test -if test "x$avalon$avalon2$avalon4$avalon7$avalon8$avalon_miner$bab$bflsc$bitforce$bitfury$bitfury16$blockerupter$hashfast$hashratio$icarus$klondike$knc$modminer$drillbit$minion$cointerra$bitmine_A1$ants1$ants2$ants3$sp10$sp30" = xnonononononononononononononononononononononononononono; then +if test "x$avalon$avalon2$avalon4$avalon7$avalon8$avalon9$avalon_miner$bab$bflsc$bitforce$bitfury$bitfury16$blockerupter$hashfast$hashratio$icarus$klondike$knc$modminer$drillbit$minion$cointerra$bitmine_A1$ants1$ants2$ants3$sp10$sp30" = xnononononononononononononononononononononononononononono; then echo AC_MSG_ERROR([No mining devices configured in]) echo diff --git a/driver-avalon9.c b/driver-avalon9.c new file mode 100644 index 0000000000..c3bd9b531c --- /dev/null +++ b/driver-avalon9.c @@ -0,0 +1,2768 @@ +/* + * Copyright 2016-2017 Mikeqin + * Copyright 2016 Con Kolivas + * + * 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 +#include "config.h" + +#include "miner.h" +#include "driver-avalon9.h" +#include "crc.h" +#include "sha2.h" +#include "hexdump.c" + +#define get_fan_pwm(v) (AVA9_PWM_MAX - (v) * AVA9_PWM_MAX / 100) + +int opt_avalon9_temp_target = AVA9_DEFAULT_TEMP_TARGET; + +int opt_avalon9_fan_min = AVA9_DEFAULT_FAN_MIN; +int opt_avalon9_fan_max = AVA9_DEFAULT_FAN_MAX; + +int opt_avalon9_voltage_level = AVA9_INVALID_VOLTAGE_LEVEL; +int opt_avalon9_voltage_level_offset = AVA9_DEFAULT_VOLTAGE_LEVEL_OFFSET; + +int opt_avalon9_freq[AVA9_DEFAULT_PLL_CNT] = +{ + AVA9_DEFAULT_FREQUENCY_IGNORE, + AVA9_DEFAULT_FREQUENCY_IGNORE, + AVA9_DEFAULT_FREQUENCY_IGNORE, + AVA9_DEFAULT_FREQUENCY_IGNORE, + AVA9_DEFAULT_FREQUENCY_IGNORE, + AVA9_DEFAULT_FREQUENCY_IGNORE, + AVA9_DEFAULT_FREQUENCY_IGNORE +}; + +int opt_avalon9_freq_sel = AVA9_DEFAULT_FREQUENCY_SEL; + +int opt_avalon9_polling_delay = AVA9_DEFAULT_POLLING_DELAY; + +int opt_avalon9_aucspeed = AVA9_AUC_SPEED; +int opt_avalon9_aucxdelay = AVA9_AUC_XDELAY; + +int opt_avalon9_smart_speed = AVA9_DEFAULT_SMART_SPEED; +/* + * smart speed have 2 modes + * 1. auto speed by A3206 chips + * 2. option 1 + adjust by average frequency + */ +bool opt_avalon9_iic_detect = AVA9_DEFAULT_IIC_DETECT; + +uint32_t opt_avalon9_th_pass = AVA9_DEFAULT_TH_PASS; +uint32_t opt_avalon9_th_fail = AVA9_DEFAULT_TH_FAIL; +uint32_t opt_avalon9_th_init = AVA9_DEFAULT_TH_INIT; +uint32_t opt_avalon9_th_ms = AVA9_DEFAULT_TH_MS; +uint32_t opt_avalon9_th_timeout = AVA9_DEFAULT_TH_TIMEOUT; +uint32_t opt_avalon9_th_add = AVA9_DEFAULT_TH_ADD; +uint32_t opt_avalon9_th_mssel = AVA9_DEFAULT_TH_MSSEL; +uint32_t opt_avalon9_nonce_mask = AVA9_DEFAULT_NONCE_MASK; +uint32_t opt_avalon9_nonce_check = AVA9_DEFAULT_NONCE_CHECK; +uint32_t opt_avalon9_mux_l2h = AVA9_DEFAULT_MUX_L2H; +uint32_t opt_avalon9_mux_h2l = AVA9_DEFAULT_MUX_H2L; +uint32_t opt_avalon9_h2ltime0_spd = AVA9_DEFAULT_H2LTIME0_SPD; +uint32_t opt_avalon9_roll_enable = AVA9_DEFAULT_ROLL_ENABLE; +uint32_t opt_avalon9_spdlow = AVA9_DEFAULT_SPDLOW; +uint32_t opt_avalon9_spdhigh = AVA9_DEFAULT_SPDHIGH; + +uint32_t opt_avalon9_lv2_th_ms = AVA9_DEFAULT_LV2_TH_MS; +uint32_t opt_avalon9_lv3_th_ms = AVA9_DEFAULT_LV3_TH_MS; +uint32_t opt_avalon9_lv4_th_ms = AVA9_DEFAULT_LV4_TH_MS; +uint32_t opt_avalon9_lv5_th_ms = AVA9_DEFAULT_LV5_TH_MS; +uint32_t opt_avalon9_lv6_th_ms = AVA9_DEFAULT_LV6_TH_MS; +uint32_t opt_avalon9_lv7_th_ms = AVA9_DEFAULT_LV7_TH_MS; +uint32_t opt_avalon9_lv2_th_add = AVA9_DEFAULT_LV2_TH_ADD; +uint32_t opt_avalon9_lv3_th_add = AVA9_DEFAULT_LV3_TH_ADD; +uint32_t opt_avalon9_lv4_th_add = AVA9_DEFAULT_LV4_TH_ADD; +uint32_t opt_avalon9_lv5_th_add = AVA9_DEFAULT_LV5_TH_ADD; +uint32_t opt_avalon9_lv6_th_add = AVA9_DEFAULT_LV6_TH_ADD; +uint32_t opt_avalon9_lv7_th_add = AVA9_DEFAULT_LV7_TH_ADD; + +uint32_t cpm_table[] = +{ + 0x00000000, + 0x0c041205, + 0x0c041203, + 0x0c031103, + 0x0c041103, + 0x0c079183, + 0x0c079503, + 0x0c07ed83, + 0x0c040603, + 0x0c06c703, + 0x0c078703, + 0x0c042583, + 0x0c078683, + 0x0c068603, + 0x0c070603, + 0x0c078603, + 0x0c040503, + 0x0c044503, + 0x0c048503, + 0x0c04c503, + 0x0c050503, + 0x0c054503, + 0x0c058503, + 0x0c05c503, + 0x0c060503, + 0x0c064503, + 0x0c068503, + 0x0c06c503, + 0x0c070503, + 0x0c074503, + 0x0c078503, + 0x0c07c503, + 0x0c040483, + 0x0c042483, + 0x0c044483, + 0x0c046483, + 0x0c048483, + 0x0c04a483, + 0x0c04c483, + 0x0c04e483, + 0x0c050483, + 0x0c052483, + 0x0c054483, + 0x0c056483, + 0x0c058483, + 0x0c05a483, + 0x0c05c483, + 0x0c05e483, + 0x0c060483, + 0x0c062483, + 0x0c064483, + 0x0c066483, + 0x0c068483, + 0x0c06a483, + 0x0c06c483, + 0x0c06e483 +}; + +struct avalon9_dev_description avalon9_dev_table[] = { + { + "921", + 921, + 4, + 26, + AVA9_MM921_VIN_ADC_RATIO, + AVA9_MM921_VOUT_ADC_RATIO, + -2, + { + AVA9_DEFAULT_FREQUENCY_0M, + AVA9_DEFAULT_FREQUENCY_0M, + AVA9_DEFAULT_FREQUENCY_0M, + AVA9_DEFAULT_FREQUENCY_0M, + AVA9_DEFAULT_FREQUENCY_750M, + AVA9_DEFAULT_FREQUENCY_775M, + AVA9_DEFAULT_FREQUENCY_800M + } + } +}; + +static uint32_t api_get_cpm(uint32_t freq) +{ + return cpm_table[freq / 25]; +} + +static uint32_t encode_voltage(int volt_level) +{ + if (volt_level > AVA9_DEFAULT_VOLTAGE_LEVEL_MAX) + volt_level = AVA9_DEFAULT_VOLTAGE_LEVEL_MAX; + else if (volt_level < AVA9_DEFAULT_VOLTAGE_LEVEL_MIN) + volt_level = AVA9_DEFAULT_VOLTAGE_LEVEL_MIN; + + if (volt_level < 0) + return 0x8080 | (-volt_level); + + return 0x8000 | volt_level; +} + +static uint32_t decode_voltage(struct avalon9_info *info, int modular_id, uint32_t volt) +{ + return (volt * info->vout_adc_ratio[modular_id] / info->asic_count[modular_id] / 100); +} + +static uint16_t decode_vin(struct avalon9_info *info, int modular_id, uint16_t volt) +{ + return (volt * info->vin_adc_ratio[modular_id] / 1000); +} + +static double decode_pvt_temp(uint16_t pvt_code) +{ + double g = 60.0; + double h = 200.0; + double cal5 = 4094.0; + double j = -0.1; + double fclkm = 6.25; + + /* Mode2 temperature equation */ + return g + h * (pvt_code / cal5 - 0.5) + j * fclkm; +} + +static uint32_t decode_pvt_volt(uint16_t volt) +{ + double vref = 1.20; + double r = 16384.0; /* 2 ** 14 */ + double c; + + c = vref / 5.0 * (6 * (volt - 0.5) / r - 1.0); + + if (c < 0) + c = 0; + + return c * 1000; +} + +#define SERIESRESISTOR 10000 +#define THERMISTORNOMINAL 10000 +#define BCOEFFICIENT 3500 +#define TEMPERATURENOMINAL 25 +float decode_auc_temp(int value) +{ + float ret, resistance; + + if (!((value > 0) && (value < 33000))) + return -273; + + resistance = (3.3 * 10000 / value) - 1; + resistance = SERIESRESISTOR / resistance; + ret = resistance / THERMISTORNOMINAL; + ret = logf(ret); + ret /= BCOEFFICIENT; + ret += 1.0 / (TEMPERATURENOMINAL + 273.15); + ret = 1.0 / ret; + ret -= 273.15; + + return ret; +} + +#define UNPACK32(x, str) \ +{ \ + *((str) + 3) = (uint8_t) ((x) ); \ + *((str) + 2) = (uint8_t) ((x) >> 8); \ + *((str) + 1) = (uint8_t) ((x) >> 16); \ + *((str) + 0) = (uint8_t) ((x) >> 24); \ +} + +static inline void sha256_prehash(const unsigned char *message, unsigned int len, unsigned char *digest) +{ + int i; + sha256_ctx ctx; + + sha256_init(&ctx); + sha256_update(&ctx, message, len); + + for (i = 0; i < 8; i++) + UNPACK32(ctx.h[i], &digest[i << 2]); +} + +char *set_avalon9_fan(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No value passed to avalon9-fan"; + if (ret == 1) + val2 = val1; + + if (val1 < 0 || val1 > 100 || val2 < 0 || val2 > 100 || val2 < val1) + return "Invalid value passed to avalon9-fan"; + + opt_avalon9_fan_min = val1; + opt_avalon9_fan_max = val2; + + return NULL; +} + +char *set_avalon9_freq(char *arg) +{ + int val[AVA9_DEFAULT_PLL_CNT]; + char *colon, *data; + int i; + + if (!(*arg)) + return NULL; + + data = arg; + memset(val, 0, sizeof(val)); + + for (i = 0; i < AVA9_DEFAULT_PLL_CNT; i++) { + colon = strchr(data, ':'); + if (colon) + *(colon++) = '\0'; + else { + /* last value */ + if (*data) { + val[i] = atoi(data); + if (val[i] > AVA9_DEFAULT_FREQUENCY_MAX) + return "Invalid value passed to avalon9-freq"; + } + break; + } + + if (*data) { + val[i] = atoi(data); + if (val[i] > AVA9_DEFAULT_FREQUENCY_MAX) + return "Invalid value passed to avalon9-freq"; + } + data = colon; + } + + for (i = 0; i < AVA9_DEFAULT_PLL_CNT; i++) + opt_avalon9_freq[i] = val[i]; + + return NULL; +} + +char *set_avalon9_voltage_level(char *arg) +{ + int val, ret; + + ret = sscanf(arg, "%d", &val); + if (ret < 1) + return "No value passed to avalon9-voltage-level"; + + if (val < AVA9_DEFAULT_VOLTAGE_LEVEL_MIN || val > AVA9_DEFAULT_VOLTAGE_LEVEL_MAX) + return "Invalid value passed to avalon9-voltage-level"; + + opt_avalon9_voltage_level = val; + + return NULL; +} + +char *set_avalon9_voltage_level_offset(char *arg) +{ + int val, ret; + + ret = sscanf(arg, "%d", &val); + if (ret < 1) + return "No value passed to avalon9-voltage-level-offset"; + + if (val < AVA9_DEFAULT_VOLTAGE_LEVEL_OFFSET_MIN || val > AVA9_DEFAULT_VOLTAGE_LEVEL_OFFSET_MAX) + return "Invalid value passed to avalon9-voltage-level-offset"; + + opt_avalon9_voltage_level_offset = val; + + return NULL; +} + +static int avalon9_init_pkg(struct avalon9_pkg *pkg, uint8_t type, uint8_t idx, uint8_t cnt) +{ + unsigned short crc; + + pkg->head[0] = AVA9_H1; + pkg->head[1] = AVA9_H2; + + pkg->type = type; + pkg->opt = 0; + pkg->idx = idx; + pkg->cnt = cnt; + + crc = crc16(pkg->data, AVA9_P_DATA_LEN); + + pkg->crc[0] = (crc & 0xff00) >> 8; + pkg->crc[1] = crc & 0xff; + + return 0; +} + +static int job_idcmp(uint8_t *job_id, char *pool_job_id) +{ + int job_id_len; + unsigned short crc, crc_expect; + + if (!pool_job_id) + return 1; + + job_id_len = strlen(pool_job_id); + crc_expect = crc16((unsigned char *)pool_job_id, job_id_len); + + crc = job_id[0] << 8 | job_id[1]; + + if (crc_expect == crc) + return 0; + + applog(LOG_DEBUG, "avalon9: job_id doesn't match! [%04x:%04x (%s)]", + crc, crc_expect, pool_job_id); + + return 1; +} + +static inline int get_temp_max(struct avalon9_info *info, int addr) +{ + int i, j; + int max = -273; + + for (i = 0; i < info->miner_count[addr]; i++) { + for (j = 0; j < info->asic_count[addr]; j++) { + if (info->temp[addr][i][j] > max) + max = info->temp[addr][i][j]; + } + } + + if (max < info->temp_mm[addr]) + max = info->temp_mm[addr]; + + return max; +} + +/* Use a PID-like feedback mechanism for optimal temperature and fan speed */ +static inline uint32_t adjust_fan(struct avalon9_info *info, int id) +{ + int t, tdiff, delta; + uint32_t pwm; + time_t now_t; + + now_t = time(NULL); + t = get_temp_max(info, id); + tdiff = t - info->temp_last_max[id]; + if (!tdiff && now_t < info->last_temp_time[id] + AVA9_DEFAULT_FAN_INTERVAL) + goto out; + info->last_temp_time[id] = now_t; + delta = t - info->temp_target[id]; + + /* Check for init value and ignore it */ + if (unlikely(info->temp_last_max[id] == -273)) + tdiff = 0; + info->temp_last_max[id] = t; + + if (t >= info->temp_overheat[id]) { + /* Hit the overheat temperature limit */ + if (info->fan_pct[id] < opt_avalon9_fan_max) { + applog(LOG_WARNING, "Overheat detected on AV8-%d, increasing fan to max", id); + info->fan_pct[id] = opt_avalon9_fan_max; + } + } else if (delta > 0) { + /* Over target temperature. */ + + /* Is the temp already coming down */ + if (tdiff < 0) + goto out; + /* Adjust fanspeed by temperature over and any further rise */ + info->fan_pct[id] += opt_avalon9_fan_max; /* info->fan_pct[id] += delta + tdiff; */ + } else { + /* Below target temperature */ + int diff = delta; /* int diff = tdiff; Sth. wrong with tdiff? It will keep lower than target */ + + if (tdiff > 0) { + int divisor = -delta / AVA9_DEFAULT_TEMP_HYSTERESIS + 1; + + /* Adjust fanspeed by temperature change proportional to + * diff from optimal. */ + diff /= divisor; + } else { + /* Is the temp below optimal and unchanging, gently lower speed */ + if (t < info->temp_target[id] - AVA9_DEFAULT_TEMP_HYSTERESIS && !tdiff) + diff -= 1; + diff = (delta == 0) ? 0 : diff; + } + info->fan_pct[id] += diff; + } + + if (info->fan_pct[id] > opt_avalon9_fan_max) + info->fan_pct[id] = opt_avalon9_fan_max; + else if (info->fan_pct[id] < opt_avalon9_fan_min) + info->fan_pct[id] = opt_avalon9_fan_min; +out: + pwm = get_fan_pwm(info->fan_pct[id]); + + if (info->cutoff[id]) + pwm = get_fan_pwm(opt_avalon9_fan_max); + + applog(LOG_DEBUG, "[%d], Adjust_fan: %dC-%d%%(%03x)", id, t, info->fan_pct[id], pwm); + + return pwm; +} + +static int decode_pkg(struct cgpu_info *avalon9, struct avalon9_ret *ar, int modular_id) +{ + struct avalon9_info *info = avalon9->device_data; + struct pool *pool, *real_pool; + struct pool *pool_stratum0 = &info->pool0; + struct pool *pool_stratum1 = &info->pool1; + struct pool *pool_stratum2 = &info->pool2; + struct thr_info *thr = NULL; + + unsigned short expected_crc; + unsigned short actual_crc; + uint32_t nonce, nonce2, ntime, miner, chip_id, tmp; + uint8_t job_id[2]; + int pool_no; + uint32_t i, j; + int64_t last_diff1; + uint16_t vin; + + if (likely(avalon9->thr)) + thr = avalon9->thr[0]; + if (ar->head[0] != AVA9_H1 && ar->head[1] != AVA9_H2) { + applog(LOG_DEBUG, "%s-%d-%d: H1 %02x, H2 %02x", + avalon9->drv->name, avalon9->device_id, modular_id, + ar->head[0], ar->head[1]); + hexdump(ar->data, 32); + return 1; + } + + expected_crc = crc16(ar->data, AVA9_P_DATA_LEN); + actual_crc = ((ar->crc[0] & 0xff) << 8) | (ar->crc[1] & 0xff); + if (expected_crc != actual_crc) { + applog(LOG_DEBUG, "%s-%d-%d: %02x: expected crc(%04x), actual_crc(%04x)", + avalon9->drv->name, avalon9->device_id, modular_id, + ar->type, expected_crc, actual_crc); + return 1; + } + + switch(ar->type) { + case AVA9_P_NONCE: + applog(LOG_DEBUG, "%s-%d-%d: AVA9_P_NONCE", avalon9->drv->name, avalon9->device_id, modular_id); + memcpy(&miner, ar->data + 0, 4); + memcpy(&nonce2, ar->data + 4, 4); + memcpy(&ntime, ar->data + 8, 4); + memcpy(&nonce, ar->data + 12, 4); + job_id[0] = ar->data[16]; + job_id[1] = ar->data[17]; + pool_no = (ar->data[18] | (ar->data[19] << 8)); + + miner = be32toh(miner); + chip_id = (miner >> 16) & 0xffff; + miner &= 0xffff; + ntime = be32toh(ntime); + if (miner >= info->miner_count[modular_id] || + pool_no >= total_pools || pool_no < 0) { + applog(LOG_DEBUG, "%s-%d-%d: Wrong miner/pool_no %d/%d", + avalon9->drv->name, avalon9->device_id, modular_id, + miner, pool_no); + break; + } + nonce2 = be32toh(nonce2); + nonce = be32toh(nonce); + + if (ntime > info->max_ntime) + info->max_ntime = ntime; + + applog(LOG_NOTICE, "%s-%d-%d: Found! P:%d - N2:%08x N:%08x NR:%d/%d [M:%d, A:%d, C:%d - MW: (%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64")]", + avalon9->drv->name, avalon9->device_id, modular_id, + pool_no, nonce2, nonce, ntime, info->max_ntime, + miner, chip_id, nonce & 0x7f, + info->chip_matching_work[modular_id][miner][0], + info->chip_matching_work[modular_id][miner][1], + info->chip_matching_work[modular_id][miner][2], + info->chip_matching_work[modular_id][miner][3]); + + real_pool = pool = pools[pool_no]; + if (job_idcmp(job_id, pool->swork.job_id)) { + if (!job_idcmp(job_id, pool_stratum0->swork.job_id)) { + applog(LOG_DEBUG, "%s-%d-%d: Match to previous stratum0! (%s)", + avalon9->drv->name, avalon9->device_id, modular_id, + pool_stratum0->swork.job_id); + pool = pool_stratum0; + } else if (!job_idcmp(job_id, pool_stratum1->swork.job_id)) { + applog(LOG_DEBUG, "%s-%d-%d: Match to previous stratum1! (%s)", + avalon9->drv->name, avalon9->device_id, modular_id, + pool_stratum1->swork.job_id); + pool = pool_stratum1; + } else if (!job_idcmp(job_id, pool_stratum2->swork.job_id)) { + applog(LOG_DEBUG, "%s-%d-%d: Match to previous stratum2! (%s)", + avalon9->drv->name, avalon9->device_id, modular_id, + pool_stratum2->swork.job_id); + pool = pool_stratum2; + } else { + applog(LOG_ERR, "%s-%d-%d: Cannot match to any stratum! (%s)", + avalon9->drv->name, avalon9->device_id, modular_id, + pool->swork.job_id); + if (likely(thr)) + inc_hw_errors(thr); + info->hw_works_i[modular_id][miner]++; + break; + } + } + + /* Can happen during init sequence before add_cgpu */ + if (unlikely(!thr)) + break; + + last_diff1 = avalon9->diff1; + if (!submit_nonce2_nonce(thr, pool, real_pool, nonce2, nonce, ntime)) + info->hw_works_i[modular_id][miner]++; + else { + info->diff1[modular_id] += (avalon9->diff1 - last_diff1); + info->chip_matching_work[modular_id][miner][chip_id]++; + } + break; + case AVA9_P_STATUS: + applog(LOG_DEBUG, "%s-%d-%d: AVA9_P_STATUS", avalon9->drv->name, avalon9->device_id, modular_id); + hexdump(ar->data, 32); + memcpy(&tmp, ar->data, 4); + tmp = be32toh(tmp); + info->temp_mm[modular_id] = tmp; + avalon9->temp = decode_auc_temp(info->auc_sensor); + + memcpy(&tmp, ar->data + 4, 4); + tmp = be32toh(tmp); + info->fan_cpm[modular_id] = tmp; + + memcpy(&tmp, ar->data + 8, 4); + info->local_works_i[modular_id][ar->idx] += be32toh(tmp); + + memcpy(&tmp, ar->data + 12, 4); + info->hw_works_i[modular_id][ar->idx] += be32toh(tmp); + + memcpy(&tmp, ar->data + 16, 4); + info->error_code[modular_id][ar->idx] = be32toh(tmp); + + memcpy(&tmp, ar->data + 20, 4); + info->error_code[modular_id][ar->cnt] = be32toh(tmp); + + memcpy(&tmp, ar->data + 24, 4); + info->error_crc[modular_id][ar->idx] += be32toh(tmp); + break; + case AVA9_P_STATUS_PMU: + /* TODO: decode ntc led from PMU */ + applog(LOG_DEBUG, "%s-%d-%d: AVA9_P_STATUS_PMU", avalon9->drv->name, avalon9->device_id, modular_id); + info->power_good[modular_id] = ar->data[16]; + for (i = 0; i < AVA9_DEFAULT_PMU_CNT; i++) { + memcpy(&info->pmu_version[modular_id][i], ar->data + 24 + (i * 4), 4); + info->pmu_version[modular_id][i][4] = '\0'; + } + + for (i = 0; i < info->miner_count[modular_id]; i++) { + memcpy(&vin, ar->data + 8 + i * 2, 2); + info->get_vin[modular_id][i] = decode_vin(info, modular_id, be16toh(vin)); + } + break; + case AVA9_P_STATUS_VOLT: + applog(LOG_DEBUG, "%s-%d-%d: AVA9_P_STATUS_VOLT", avalon9->drv->name, avalon9->device_id, modular_id); + for (i = 0; i < info->miner_count[modular_id]; i++) { + memcpy(&tmp, ar->data + i * 4, 4); + info->get_voltage[modular_id][i] = decode_voltage(info, modular_id, be32toh(tmp)); + } + break; + case AVA9_P_STATUS_PLL: + applog(LOG_DEBUG, "%s-%d-%d: AVA9_P_STATUS_PLL", avalon9->drv->name, avalon9->device_id, modular_id); + if (ar->opt) { + for (i = 0; i < AVA9_DEFAULT_PLL_CNT; i++) { + memcpy(&tmp, ar->data + i * 4, 4); + info->get_pll[modular_id][ar->idx][i] = be32toh(tmp); + } + } else { + for (i = 0; i < AVA9_DEFAULT_PLL_CNT; i++) { + memcpy(&tmp, ar->data + i * 4, 4); + info->get_frequency[modular_id][ar->idx][i] = be32toh(tmp); + } + } + break; + case AVA9_P_STATUS_PVT: + applog(LOG_DEBUG, "%s-%d-%d: AVA9_P_STATUS_PVT", avalon9->drv->name, avalon9->device_id, modular_id); + if (!info->asic_count[modular_id]) + break; + + if (ar->idx < info->asic_count[modular_id]) { + for (i = 0; i < info->miner_count[modular_id]; i++) { + memcpy(&tmp, ar->data + i * 4, 2); + tmp = be16toh(tmp); + info->temp[modular_id][i][ar->idx] = decode_pvt_temp(tmp); + + memcpy(&tmp, ar->data + i * 4 + 2, 2); + tmp = be16toh(tmp); + info->core_volt[modular_id][i][ar->idx] = decode_pvt_volt(tmp); + } + } + break; + case AVA9_P_STATUS_ASIC: + { + int miner_id; + int asic_id; + uint16_t freq; + + if (!info->asic_count[modular_id]) + break; + + miner_id = ar->idx / info->asic_count[modular_id]; + asic_id = ar->idx % info->asic_count[modular_id]; + + applog(LOG_DEBUG, "%s-%d-%d: AVA9_P_STATUS_ASIC %d-%d", + avalon9->drv->name, avalon9->device_id, modular_id, + miner_id, asic_id); + + memcpy(&tmp, ar->data + 0, 4); + if (tmp) + info->get_asic[modular_id][miner_id][asic_id][0] = be32toh(tmp); + + memcpy(&tmp, ar->data + 4, 4); + if (tmp) + info->get_asic[modular_id][miner_id][asic_id][1] = be32toh(tmp); + + for (i = 0; i < AVA9_DEFAULT_PLL_CNT; i++) { + memcpy(&tmp, ar->data + 8 + i * 2, 2); + info->get_asic[modular_id][miner_id][asic_id][2 + i] = be16toh(tmp); + } + } + break; + case AVA9_P_STATUS_FAC: + applog(LOG_DEBUG, "%s-%d-%d: AVA9_P_STATUS_FAC", avalon9->drv->name, avalon9->device_id, modular_id); + info->factory_info[0] = ar->data[0]; + break; + case AVA9_P_STATUS_OC: + applog(LOG_DEBUG, "%s-%d-%d: AVA9_P_STATUS_OC", avalon9->drv->name, avalon9->device_id, modular_id); + info->overclocking_info[0] = ar->data[0]; + break; + default: + applog(LOG_DEBUG, "%s-%d-%d: Unknown response %x", avalon9->drv->name, avalon9->device_id, modular_id, ar->type); + break; + } + return 0; +} + +/* + # IIC packet format: length[1]+transId[1]+sesId[1]+req[1]+data[60] + # length: 4+len(data) + # transId: 0 + # sesId: 0 + # req: checkout the header file + # data: + # INIT: clock_rate[4] + reserved[4] + payload[52] + # XFER: txSz[1]+rxSz[1]+options[1]+slaveAddr[1] + payload[56] + */ +static int avalon9_auc_init_pkg(uint8_t *iic_pkg, struct avalon9_iic_info *iic_info, uint8_t *buf, int wlen, int rlen) +{ + memset(iic_pkg, 0, AVA9_AUC_P_SIZE); + + switch (iic_info->iic_op) { + case AVA9_IIC_INIT: + iic_pkg[0] = 12; /* 4 bytes IIC header + 4 bytes speed + 4 bytes xfer delay */ + iic_pkg[3] = AVA9_IIC_INIT; + iic_pkg[4] = iic_info->iic_param.aucParam[0] & 0xff; + iic_pkg[5] = (iic_info->iic_param.aucParam[0] >> 8) & 0xff; + iic_pkg[6] = (iic_info->iic_param.aucParam[0] >> 16) & 0xff; + iic_pkg[7] = iic_info->iic_param.aucParam[0] >> 24; + iic_pkg[8] = iic_info->iic_param.aucParam[1] & 0xff; + iic_pkg[9] = (iic_info->iic_param.aucParam[1] >> 8) & 0xff; + iic_pkg[10] = (iic_info->iic_param.aucParam[1] >> 16) & 0xff; + iic_pkg[11] = iic_info->iic_param.aucParam[1] >> 24; + break; + case AVA9_IIC_XFER: + iic_pkg[0] = 8 + wlen; + iic_pkg[3] = AVA9_IIC_XFER; + iic_pkg[4] = wlen; + iic_pkg[5] = rlen; + iic_pkg[7] = iic_info->iic_param.slave_addr; + if (buf && wlen) + memcpy(iic_pkg + 8, buf, wlen); + break; + case AVA9_IIC_RESET: + case AVA9_IIC_DEINIT: + case AVA9_IIC_INFO: + iic_pkg[0] = 4; + iic_pkg[3] = iic_info->iic_op; + break; + + default: + break; + } + + return 0; +} + +static int avalon9_iic_xfer(struct cgpu_info *avalon9, uint8_t slave_addr, + uint8_t *wbuf, int wlen, + uint8_t *rbuf, int rlen) +{ + struct avalon9_info *info = avalon9->device_data; + struct i2c_ctx *pctx = NULL; + int err = 1; + bool ret = false; + + pctx = info->i2c_slaves[slave_addr]; + if (!pctx) { + applog(LOG_ERR, "%s-%d: IIC xfer i2c slaves null!", avalon9->drv->name, avalon9->device_id); + goto out; + } + + if (wbuf) { + ret = pctx->write_raw(pctx, wbuf, wlen); + if (!ret) { + applog(LOG_DEBUG, "%s-%d: IIC xfer write raw failed!", avalon9->drv->name, avalon9->device_id); + goto out; + } + } + + cgsleep_ms(5); + + if (rbuf) { + ret = pctx->read_raw(pctx, rbuf, rlen); + if (!ret) { + applog(LOG_DEBUG, "%s-%d: IIC xfer read raw failed!", avalon9->drv->name, avalon9->device_id); + hexdump(rbuf, rlen); + goto out; + } + } + + return 0; +out: + return err; +} + +static int avalon9_auc_xfer(struct cgpu_info *avalon9, + uint8_t *wbuf, int wlen, int *write, + uint8_t *rbuf, int rlen, int *read) +{ + int err = -1; + + if (unlikely(avalon9->usbinfo.nodev)) + goto out; + + usb_buffer_clear(avalon9); + err = usb_write(avalon9, (char *)wbuf, wlen, write, C_AVA9_WRITE); + if (err || *write != wlen) { + applog(LOG_DEBUG, "%s-%d: AUC xfer %d, w(%d-%d)!", avalon9->drv->name, avalon9->device_id, err, wlen, *write); + usb_nodev(avalon9); + goto out; + } + + cgsleep_ms(opt_avalon9_aucxdelay / 4800 + 1); + + rlen += 4; /* Add 4 bytes IIC header */ + err = usb_read(avalon9, (char *)rbuf, rlen, read, C_AVA9_READ); + if (err || *read != rlen || *read != rbuf[0]) { + applog(LOG_DEBUG, "%s-%d: AUC xfer %d, r(%d-%d-%d)!", avalon9->drv->name, avalon9->device_id, err, rlen - 4, *read, rbuf[0]); + hexdump(rbuf, rlen); + return -1; + } + *read = rbuf[0] - 4; /* Remove 4 bytes IIC header */ +out: + return err; +} + +static int avalon9_auc_init(struct cgpu_info *avalon9, char *ver) +{ + struct avalon9_iic_info iic_info; + int err, wlen, rlen; + uint8_t wbuf[AVA9_AUC_P_SIZE]; + uint8_t rbuf[AVA9_AUC_P_SIZE]; + + if (unlikely(avalon9->usbinfo.nodev)) + return 1; + + /* Try to clean the AUC buffer */ + usb_buffer_clear(avalon9); + err = usb_read(avalon9, (char *)rbuf, AVA9_AUC_P_SIZE, &rlen, C_AVA9_READ); + applog(LOG_DEBUG, "%s-%d: AUC usb_read %d, %d!", avalon9->drv->name, avalon9->device_id, err, rlen); + hexdump(rbuf, AVA9_AUC_P_SIZE); + + /* Reset */ + iic_info.iic_op = AVA9_IIC_RESET; + rlen = 0; + avalon9_auc_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + + memset(rbuf, 0, AVA9_AUC_P_SIZE); + err = avalon9_auc_xfer(avalon9, wbuf, AVA9_AUC_P_SIZE, &wlen, rbuf, rlen, &rlen); + if (err) { + applog(LOG_ERR, "%s-%d: Failed to reset Avalon USB2IIC Converter", avalon9->drv->name, avalon9->device_id); + return 1; + } + + /* Deinit */ + iic_info.iic_op = AVA9_IIC_DEINIT; + rlen = 0; + avalon9_auc_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + + memset(rbuf, 0, AVA9_AUC_P_SIZE); + err = avalon9_auc_xfer(avalon9, wbuf, AVA9_AUC_P_SIZE, &wlen, rbuf, rlen, &rlen); + if (err) { + applog(LOG_ERR, "%s-%d: Failed to deinit Avalon USB2IIC Converter", avalon9->drv->name, avalon9->device_id); + return 1; + } + + /* Init */ + iic_info.iic_op = AVA9_IIC_INIT; + iic_info.iic_param.aucParam[0] = opt_avalon9_aucspeed; + iic_info.iic_param.aucParam[1] = opt_avalon9_aucxdelay; + rlen = AVA9_AUC_VER_LEN; + avalon9_auc_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + + memset(rbuf, 0, AVA9_AUC_P_SIZE); + err = avalon9_auc_xfer(avalon9, wbuf, AVA9_AUC_P_SIZE, &wlen, rbuf, rlen, &rlen); + if (err) { + applog(LOG_ERR, "%s-%d: Failed to init Avalon USB2IIC Converter", avalon9->drv->name, avalon9->device_id); + return 1; + } + + hexdump(rbuf, AVA9_AUC_P_SIZE); + + memcpy(ver, rbuf + 4, AVA9_AUC_VER_LEN); + ver[AVA9_AUC_VER_LEN] = '\0'; + + applog(LOG_DEBUG, "%s-%d: USB2IIC Converter version: %s!", avalon9->drv->name, avalon9->device_id, ver); + + return 0; +} + +static int avalon9_auc_getinfo(struct cgpu_info *avalon9) +{ + struct avalon9_iic_info iic_info; + int err, wlen, rlen; + uint8_t wbuf[AVA9_AUC_P_SIZE]; + uint8_t rbuf[AVA9_AUC_P_SIZE]; + uint8_t *pdata = rbuf + 4; + uint16_t adc_val; + struct avalon9_info *info = avalon9->device_data; + + iic_info.iic_op = AVA9_IIC_INFO; + /* + * Device info: (9 bytes) + * tempadc(2), reqRdIndex, reqWrIndex, + * respRdIndex, respWrIndex, tx_flags, state + */ + rlen = 7; + avalon9_auc_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + + memset(rbuf, 0, AVA9_AUC_P_SIZE); + err = avalon9_auc_xfer(avalon9, wbuf, AVA9_AUC_P_SIZE, &wlen, rbuf, rlen, &rlen); + if (err) { + applog(LOG_ERR, "%s-%d: AUC Failed to get info ", avalon9->drv->name, avalon9->device_id); + return 1; + } + + applog(LOG_DEBUG, "%s-%d: AUC tempADC(%03d), reqcnt(%d), respcnt(%d), txflag(%d), state(%d)", + avalon9->drv->name, avalon9->device_id, + pdata[1] << 8 | pdata[0], + pdata[2], + pdata[3], + pdata[5] << 8 | pdata[4], + pdata[6]); + + adc_val = pdata[1] << 8 | pdata[0]; + + info->auc_sensor = 3.3 * adc_val * 10000 / 1023; + + return 0; +} + +static int avalon9_iic_xfer_pkg(struct cgpu_info *avalon9, uint8_t slave_addr, + const struct avalon9_pkg *pkg, struct avalon9_ret *ret) +{ + struct avalon9_iic_info iic_info; + int err, wcnt, rcnt, rlen = 0; + uint8_t wbuf[AVA9_AUC_P_SIZE]; + uint8_t rbuf[AVA9_AUC_P_SIZE]; + + struct avalon9_info *info = avalon9->device_data; + + if (ret) + rlen = AVA9_READ_SIZE; + + if (info->connecter == AVA9_CONNECTER_AUC) { + if (unlikely(avalon9->usbinfo.nodev)) + return AVA9_SEND_ERROR; + + iic_info.iic_op = AVA9_IIC_XFER; + iic_info.iic_param.slave_addr = slave_addr; + + avalon9_auc_init_pkg(wbuf, &iic_info, (uint8_t *)pkg, AVA9_WRITE_SIZE, rlen); + err = avalon9_auc_xfer(avalon9, wbuf, wbuf[0], &wcnt, rbuf, rlen, &rcnt); + if ((pkg->type != AVA9_P_DETECT) && err == -7 && !rcnt && rlen) { + avalon9_auc_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + err = avalon9_auc_xfer(avalon9, wbuf, wbuf[0], &wcnt, rbuf, rlen, &rcnt); + applog(LOG_DEBUG, "%s-%d-%d: AUC read again!(type:0x%x, err:%d)", avalon9->drv->name, avalon9->device_id, slave_addr, pkg->type, err); + } + if (err || rcnt != rlen) { + if (info->xfer_err_cnt++ == 100) { + applog(LOG_DEBUG, "%s-%d-%d: AUC xfer_err_cnt reach err = %d, rcnt = %d, rlen = %d", + avalon9->drv->name, avalon9->device_id, slave_addr, + err, rcnt, rlen); + + cgsleep_ms(5 * 1000); /* Wait MM reset */ + if (avalon9_auc_init(avalon9, info->auc_version)) { + applog(LOG_WARNING, "%s-%d: Failed to re-init auc, unplugging for new hotplug", + avalon9->drv->name, avalon9->device_id); + usb_nodev(avalon9); + } + } + return AVA9_SEND_ERROR; + } + + if (ret) + memcpy((char *)ret, rbuf + 4, AVA9_READ_SIZE); + + info->xfer_err_cnt = 0; + } + + if (info->connecter == AVA9_CONNECTER_IIC) { + err = avalon9_iic_xfer(avalon9, slave_addr, (uint8_t *)pkg, AVA9_WRITE_SIZE, (uint8_t *)ret, AVA9_READ_SIZE); + if ((pkg->type != AVA9_P_DETECT) && err) { + err = avalon9_iic_xfer(avalon9, slave_addr, (uint8_t *)pkg, AVA9_WRITE_SIZE, (uint8_t *)ret, AVA9_READ_SIZE); + applog(LOG_DEBUG, "%s-%d-%d: IIC read again!(type:0x%x, err:%d)", avalon9->drv->name, avalon9->device_id, slave_addr, pkg->type, err); + } + if (err) { + /* FIXME: Don't care broadcast message with no reply, or it will block other thread when called by avalon9_send_bc_pkgs */ + if ((pkg->type != AVA9_P_DETECT) && (slave_addr == AVA9_MODULE_BROADCAST)) + return AVA9_SEND_OK; + + if (info->xfer_err_cnt++ == 100) { + info->xfer_err_cnt = 0; + applog(LOG_DEBUG, "%s-%d-%d: IIC xfer_err_cnt reach err = %d, rcnt = %d, rlen = %d", + avalon9->drv->name, avalon9->device_id, slave_addr, + err, rcnt, rlen); + + cgsleep_ms(5 * 1000); /* Wait MM reset */ + } + return AVA9_SEND_ERROR; + } + + info->xfer_err_cnt = 0; + } + + return AVA9_SEND_OK; +} + +static int avalon9_send_bc_pkgs(struct cgpu_info *avalon9, const struct avalon9_pkg *pkg) +{ + int ret; + + do { + ret = avalon9_iic_xfer_pkg(avalon9, AVA9_MODULE_BROADCAST, pkg, NULL); + } while (ret != AVA9_SEND_OK); + + return 0; +} + +static void avalon9_stratum_pkgs(struct cgpu_info *avalon9, struct pool *pool) +{ + struct avalon9_info *info = avalon9->device_data; + const int merkle_offset = 36; + struct avalon9_pkg pkg; + int i, a, b; + uint32_t tmp; + unsigned char target[32]; + int job_id_len, n2size; + unsigned short crc; + int coinbase_len_posthash, coinbase_len_prehash; + uint8_t coinbase_prehash[32]; + uint32_t range, start; + + /* Send out the first stratum message STATIC */ + applog(LOG_DEBUG, "%s-%d: Pool stratum message STATIC: %d, %d, %d, %d, %d", + avalon9->drv->name, avalon9->device_id, + pool->coinbase_len, + pool->nonce2_offset, + pool->n2size, + merkle_offset, + pool->merkles); + memset(pkg.data, 0, AVA9_P_DATA_LEN); + tmp = be32toh(pool->coinbase_len); + memcpy(pkg.data, &tmp, 4); + + tmp = be32toh(pool->nonce2_offset); + memcpy(pkg.data + 4, &tmp, 4); + + n2size = pool->n2size >= 4 ? 4 : pool->n2size; + tmp = be32toh(n2size); + memcpy(pkg.data + 8, &tmp, 4); + + tmp = be32toh(merkle_offset); + memcpy(pkg.data + 12, &tmp, 4); + + tmp = be32toh(pool->merkles); + memcpy(pkg.data + 16, &tmp, 4); + + if (pool->n2size == 3) + range = 0xffffff / (total_devices ? total_devices : 1); + else + range = 0xffffffff / (total_devices ? total_devices : 1); + start = range * avalon9->device_id; + + tmp = be32toh(start); + memcpy(pkg.data + 20, &tmp, 4); + + tmp = be32toh(range); + memcpy(pkg.data + 24, &tmp, 4); + + if (info->work_restart) { + info->work_restart = false; + tmp = be32toh(0x1); + memcpy(pkg.data + 28, &tmp, 4); + } + + avalon9_init_pkg(&pkg, AVA9_P_STATIC, 1, 1); + if (avalon9_send_bc_pkgs(avalon9, &pkg)) + return; + + if (pool->sdiff <= AVA9_DRV_DIFFMAX) + set_target(target, pool->sdiff); + else + set_target(target, AVA9_DRV_DIFFMAX); + + memcpy(pkg.data, target, 32); + if (opt_debug) { + char *target_str; + target_str = bin2hex(target, 32); + applog(LOG_DEBUG, "%s-%d: Pool stratum target: %s", avalon9->drv->name, avalon9->device_id, target_str); + free(target_str); + } + avalon9_init_pkg(&pkg, AVA9_P_TARGET, 1, 1); + if (avalon9_send_bc_pkgs(avalon9, &pkg)) + return; + + memset(pkg.data, 0, AVA9_P_DATA_LEN); + + job_id_len = strlen(pool->swork.job_id); + crc = crc16((unsigned char *)pool->swork.job_id, job_id_len); + applog(LOG_DEBUG, "%s-%d: Pool stratum message JOBS_ID[%04x]: %s", + avalon9->drv->name, avalon9->device_id, + crc, pool->swork.job_id); + tmp = ((crc << 16) | pool->pool_no); + if (info->last_jobid != tmp) { + info->last_jobid = tmp; + pkg.data[0] = (crc & 0xff00) >> 8; + pkg.data[1] = crc & 0xff; + pkg.data[2] = pool->pool_no & 0xff; + pkg.data[3] = (pool->pool_no & 0xff00) >> 8; + avalon9_init_pkg(&pkg, AVA9_P_JOB_ID, 1, 1); + if (avalon9_send_bc_pkgs(avalon9, &pkg)) + return; + } + + coinbase_len_prehash = pool->nonce2_offset - (pool->nonce2_offset % SHA256_BLOCK_SIZE); + coinbase_len_posthash = pool->coinbase_len - coinbase_len_prehash; + sha256_prehash(pool->coinbase, coinbase_len_prehash, coinbase_prehash); + + a = (coinbase_len_posthash / AVA9_P_DATA_LEN) + 1; + b = coinbase_len_posthash % AVA9_P_DATA_LEN; + memcpy(pkg.data, coinbase_prehash, 32); + avalon9_init_pkg(&pkg, AVA9_P_COINBASE, 1, a + (b ? 1 : 0)); + if (avalon9_send_bc_pkgs(avalon9, &pkg)) + return; + + applog(LOG_DEBUG, "%s-%d: Pool stratum message modified COINBASE: %d %d", + avalon9->drv->name, avalon9->device_id, + a, b); + for (i = 1; i < a; i++) { + memcpy(pkg.data, pool->coinbase + coinbase_len_prehash + i * 32 - 32, 32); + avalon9_init_pkg(&pkg, AVA9_P_COINBASE, i + 1, a + (b ? 1 : 0)); + if (avalon9_send_bc_pkgs(avalon9, &pkg)) + return; + } + if (b) { + memset(pkg.data, 0, AVA9_P_DATA_LEN); + memcpy(pkg.data, pool->coinbase + coinbase_len_prehash + i * 32 - 32, b); + avalon9_init_pkg(&pkg, AVA9_P_COINBASE, i + 1, i + 1); + if (avalon9_send_bc_pkgs(avalon9, &pkg)) + return; + } + + b = pool->merkles; + applog(LOG_DEBUG, "%s-%d: Pool stratum message MERKLES: %d", avalon9->drv->name, avalon9->device_id, b); + for (i = 0; i < b; i++) { + memset(pkg.data, 0, AVA9_P_DATA_LEN); + memcpy(pkg.data, pool->swork.merkle_bin[i], 32); + avalon9_init_pkg(&pkg, AVA9_P_MERKLES, i + 1, b); + if (avalon9_send_bc_pkgs(avalon9, &pkg)) + return; + } + + applog(LOG_DEBUG, "%s-%d: Pool stratum message HEADER: 4", avalon9->drv->name, avalon9->device_id); + for (i = 0; i < 4; i++) { + memset(pkg.data, 0, AVA9_P_DATA_LEN); + memcpy(pkg.data, pool->header_bin + i * 32, 32); + avalon9_init_pkg(&pkg, AVA9_P_HEADER, i + 1, 4); + if (avalon9_send_bc_pkgs(avalon9, &pkg)) + return; + } + + if (info->connecter == AVA9_CONNECTER_AUC) + avalon9_auc_getinfo(avalon9); +} + +static struct cgpu_info *avalon9_iic_detect(void) +{ + int i; + struct avalon9_info *info; + struct cgpu_info *avalon9 = NULL; + struct i2c_ctx *i2c_slave = NULL; + + i2c_slave = i2c_slave_open(I2C_BUS, 0); + if (!i2c_slave) { + applog(LOG_ERR, "avalon9 init iic failed\n"); + return NULL; + } + + i2c_slave->exit(i2c_slave); + i2c_slave = NULL; + + avalon9 = cgcalloc(1, sizeof(*avalon9)); + avalon9->drv = &avalon9_drv; + avalon9->deven = DEV_ENABLED; + avalon9->threads = 1; + add_cgpu(avalon9); + + applog(LOG_INFO, "%s-%d: Found at %s", avalon9->drv->name, avalon9->device_id, + I2C_BUS); + + avalon9->device_data = cgcalloc(sizeof(struct avalon9_info), 1); + memset(avalon9->device_data, 0, sizeof(struct avalon9_info)); + info = avalon9->device_data; + + for (i = 0; i < AVA9_DEFAULT_MODULARS; i++) { + info->enable[i] = false; + info->reboot[i] = false; + info->i2c_slaves[i] = i2c_slave_open(I2C_BUS, i); + if (!info->i2c_slaves[i]) { + applog(LOG_ERR, "avalon9 init i2c slaves failed\n"); + free(avalon9->device_data); + avalon9->device_data = NULL; + free(avalon9); + avalon9 = NULL; + return NULL; + } + } + + info->connecter = AVA9_CONNECTER_IIC; + + return avalon9; +} + +static void detect_modules(struct cgpu_info *avalon9); + +static struct cgpu_info *avalon9_auc_detect(struct libusb_device *dev, struct usb_find_devices *found) +{ + int i, modules = 0; + struct avalon9_info *info; + struct cgpu_info *avalon9 = usb_alloc_cgpu(&avalon9_drv, 1); + char auc_ver[AVA9_AUC_VER_LEN]; + + if (!usb_init(avalon9, dev, found)) { + applog(LOG_ERR, "avalon9 failed usb_init"); + avalon9 = usb_free_cgpu(avalon9); + return NULL; + } + + /* avalon9 prefers not to use zero length packets */ + avalon9->nozlp = true; + + /* We try twice on AUC init */ + if (avalon9_auc_init(avalon9, auc_ver) && avalon9_auc_init(avalon9, auc_ver)) + return NULL; + + applog(LOG_INFO, "%s-%d: Found at %s", avalon9->drv->name, avalon9->device_id, + avalon9->device_path); + + avalon9->device_data = cgcalloc(sizeof(struct avalon9_info), 1); + memset(avalon9->device_data, 0, sizeof(struct avalon9_info)); + info = avalon9->device_data; + memcpy(info->auc_version, auc_ver, AVA9_AUC_VER_LEN); + info->auc_version[AVA9_AUC_VER_LEN] = '\0'; + info->auc_speed = opt_avalon9_aucspeed; + info->auc_xdelay = opt_avalon9_aucxdelay; + + for (i = 0; i < AVA9_DEFAULT_MODULARS; i++) + info->enable[i] = 0; + + info->connecter = AVA9_CONNECTER_AUC; + + detect_modules(avalon9); + for (i = 0; i < AVA9_DEFAULT_MODULARS; i++) + modules += info->enable[i]; + + if (!modules) { + applog(LOG_INFO, "avalon9 found but no modules initialised"); + free(info); + avalon9 = usb_free_cgpu(avalon9); + return NULL; + } + + /* We have an avalon9 AUC connected */ + avalon9->threads = 1; + add_cgpu(avalon9); + + update_usb_stats(avalon9); + + return avalon9; +} + +static inline void avalon9_detect(bool __maybe_unused hotplug) +{ + usb_detect(&avalon9_drv, avalon9_auc_detect); + if (!hotplug && opt_avalon9_iic_detect) + avalon9_iic_detect(); +} + +static bool avalon9_prepare(struct thr_info *thr) +{ + struct cgpu_info *avalon9 = thr->cgpu; + struct avalon9_info *info = avalon9->device_data; + + info->last_diff1 = 0; + info->pending_diff1 = 0; + info->last_rej = 0; + info->mm_count = 0; + info->xfer_err_cnt = 0; + info->pool_no = 0; + + memset(&(info->firsthash), 0, sizeof(info->firsthash)); + cgtime(&(info->last_fan_adj)); + cgtime(&info->last_stratum); + cgtime(&info->last_detect); + + cglock_init(&info->update_lock); + cglock_init(&info->pool0.data_lock); + cglock_init(&info->pool1.data_lock); + cglock_init(&info->pool2.data_lock); + + return true; +} + +static int check_module_exist(struct cgpu_info *avalon9, uint8_t mm_dna[AVA9_MM_DNA_LEN]) +{ + struct avalon9_info *info = avalon9->device_data; + int i; + + for (i = 0; i < AVA9_DEFAULT_MODULARS; i++) { + /* last byte is \0 */ + if (info->enable[i] && !memcmp(info->mm_dna[i], mm_dna, AVA9_MM_DNA_LEN)) + return 1; + } + + return 0; +} + +static void detect_modules(struct cgpu_info *avalon9) +{ + struct avalon9_info *info = avalon9->device_data; + struct avalon9_pkg send_pkg; + struct avalon9_ret ret_pkg; + uint32_t tmp; + int i, j, k, err, rlen; + uint8_t dev_index; + uint8_t rbuf[AVA9_AUC_P_SIZE]; + + /* Detect new modules here */ + for (i = 1; i < AVA9_DEFAULT_MODULARS + 1; i++) { + if (info->enable[i]) + continue; + + /* Send out detect pkg */ + applog(LOG_DEBUG, "%s-%d: AVA9_P_DETECT ID[%d]", + avalon9->drv->name, avalon9->device_id, i); + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + tmp = be32toh(i); /* ID */ + memcpy(send_pkg.data + 28, &tmp, 4); + avalon9_init_pkg(&send_pkg, AVA9_P_DETECT, 1, 1); + err = avalon9_iic_xfer_pkg(avalon9, AVA9_MODULE_BROADCAST, &send_pkg, &ret_pkg); + if (err == AVA9_SEND_OK) { + if (decode_pkg(avalon9, &ret_pkg, AVA9_MODULE_BROADCAST)) { + applog(LOG_DEBUG, "%s-%d: Should be AVA9_P_ACKDETECT(%d), but %d", + avalon9->drv->name, avalon9->device_id, AVA9_P_ACKDETECT, ret_pkg.type); + continue; + } + } + + if (err != AVA9_SEND_OK) { + applog(LOG_DEBUG, "%s-%d: AVA9_P_DETECT: Failed AUC xfer data with err %d", + avalon9->drv->name, avalon9->device_id, err); + break; + } + + applog(LOG_DEBUG, "%s-%d: Module detect ID[%d]: %d", + avalon9->drv->name, avalon9->device_id, i, ret_pkg.type); + if (ret_pkg.type != AVA9_P_ACKDETECT) + break; + + if (check_module_exist(avalon9, ret_pkg.data)) + continue; + + /* Check count of modulars */ + if (i == AVA9_DEFAULT_MODULARS) { + applog(LOG_NOTICE, "You have connected more than %d machines. This is discouraged.", (AVA9_DEFAULT_MODULARS - 1)); + info->conn_overloaded = true; + break; + } else + info->conn_overloaded = false; + + memcpy(info->mm_version[i], ret_pkg.data + AVA9_MM_DNA_LEN, AVA9_MM_VER_LEN); + info->mm_version[i][AVA9_MM_VER_LEN] = '\0'; + for (dev_index = 0; dev_index < (sizeof(avalon9_dev_table) / sizeof(avalon9_dev_table[0])); dev_index++) { + if (!strncmp((char *)&(info->mm_version[i]), (char *)(avalon9_dev_table[dev_index].dev_id_str), 3)) { + info->mod_type[i] = avalon9_dev_table[dev_index].mod_type; + info->miner_count[i] = avalon9_dev_table[dev_index].miner_count; + info->asic_count[i] = avalon9_dev_table[dev_index].asic_count; + info->vin_adc_ratio[i] = avalon9_dev_table[dev_index].vin_adc_ratio; + info->vout_adc_ratio[i] = avalon9_dev_table[dev_index].vout_adc_ratio; + break; + } + } + if (dev_index == (sizeof(avalon9_dev_table) / sizeof(avalon9_dev_table[0]))) { + applog(LOG_NOTICE, "%s-%d: The modular version %s cann't be support", + avalon9->drv->name, avalon9->device_id, info->mm_version[i]); + break; + } + + info->enable[i] = 1; + cgtime(&info->elapsed[i]); + memcpy(info->mm_dna[i], ret_pkg.data, AVA9_MM_DNA_LEN); + memcpy(&tmp, ret_pkg.data + AVA9_MM_DNA_LEN + AVA9_MM_VER_LEN, 4); + tmp = be32toh(tmp); + info->total_asics[i] = tmp; + info->temp_overheat[i] = AVA9_DEFAULT_TEMP_OVERHEAT; + info->temp_target[i] = opt_avalon9_temp_target; + info->fan_pct[i] = opt_avalon9_fan_min; + for (j = 0; j < info->miner_count[i]; j++) { + if (opt_avalon9_voltage_level == AVA9_INVALID_VOLTAGE_LEVEL) + info->set_voltage_level[i][j] = avalon9_dev_table[dev_index].set_voltage_level; + else + info->set_voltage_level[i][j] = opt_avalon9_voltage_level; + info->get_voltage[i][j] = 0; + info->get_vin[i][j] = 0; + + for (k = 0; k < info->asic_count[i]; k++) + info->temp[i][j][k] = -273; + + for (k = 0; k < AVA9_DEFAULT_PLL_CNT; k++) + info->set_frequency[i][j][k] = avalon9_dev_table[dev_index].set_freq[k]; + } + + info->freq_mode[i] = AVA9_FREQ_INIT_MODE; + memset(info->get_pll[i], 0, sizeof(uint32_t) * info->miner_count[i] * AVA9_DEFAULT_PLL_CNT); + + info->led_indicator[i] = 0; + info->cutoff[i] = 0; + info->fan_cpm[i] = 0; + info->temp_mm[i] = -273; + info->temp_last_max[i] = -273; + info->local_works[i] = 0; + info->hw_works[i] = 0; + for (j = 0; j < info->miner_count[i]; j++) { + memset(info->chip_matching_work[i][j], 0, sizeof(uint64_t) * info->asic_count[i]); + info->local_works_i[i][j] = 0; + info->hw_works_i[i][j] = 0; + info->error_code[i][j] = 0; + info->error_crc[i][j] = 0; + } + info->error_code[i][j] = 0; + info->error_polling_cnt[i] = 0; + info->power_good[i] = 0; + memset(info->pmu_version[i], 0, sizeof(char) * 5 * AVA9_DEFAULT_PMU_CNT); + info->diff1[i] = 0; + + applog(LOG_NOTICE, "%s-%d: New module detected! ID[%d-%x]", + avalon9->drv->name, avalon9->device_id, i, info->mm_dna[i][AVA9_MM_DNA_LEN - 1]); + + /* Tell MM, it has been detected */ + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + memcpy(send_pkg.data, info->mm_dna[i], AVA9_MM_DNA_LEN); + avalon9_init_pkg(&send_pkg, AVA9_P_SYNC, 1, 1); + avalon9_iic_xfer_pkg(avalon9, i, &send_pkg, &ret_pkg); + /* Keep the usb buffer is empty */ + usb_buffer_clear(avalon9); + usb_read(avalon9, (char *)rbuf, AVA9_AUC_P_SIZE, &rlen, C_AVA9_READ); + } +} + +static void detach_module(struct cgpu_info *avalon9, int addr) +{ + struct avalon9_info *info = avalon9->device_data; + + info->enable[addr] = 0; + applog(LOG_NOTICE, "%s-%d: Module detached! ID[%d]", + avalon9->drv->name, avalon9->device_id, addr); +} + +static int polling(struct cgpu_info *avalon9) +{ + struct avalon9_info *info = avalon9->device_data; + struct avalon9_pkg send_pkg; + struct avalon9_ret ar; + int i, tmp, ret, decode_err = 0; + struct timeval current_fan; + int do_adjust_fan = 0; + uint32_t fan_pwm; + double device_tdiff; + + cgtime(¤t_fan); + device_tdiff = tdiff(¤t_fan, &(info->last_fan_adj)); + if (device_tdiff > 2.0 || device_tdiff < 0) { + cgtime(&info->last_fan_adj); + do_adjust_fan = 1; + } + + for (i = 1; i < AVA9_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + cgsleep_ms(opt_avalon9_polling_delay); + + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + /* Red LED */ + tmp = be32toh(info->led_indicator[i]); + memcpy(send_pkg.data, &tmp, 4); + + /* Adjust fan every 2 seconds*/ + if (do_adjust_fan) { + fan_pwm = adjust_fan(info, i); + fan_pwm |= 0x80000000; + tmp = be32toh(fan_pwm); + memcpy(send_pkg.data + 4, &tmp, 4); + } + + if (info->reboot[i]) { + info->reboot[i] = false; + send_pkg.data[8] = 0x1; + } + + avalon9_init_pkg(&send_pkg, AVA9_P_POLLING, 1, 1); + ret = avalon9_iic_xfer_pkg(avalon9, i, &send_pkg, &ar); + if (ret == AVA9_SEND_OK) + decode_err = decode_pkg(avalon9, &ar, i); + + if (ret != AVA9_SEND_OK || decode_err) { + info->error_polling_cnt[i]++; + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + avalon9_init_pkg(&send_pkg, AVA9_P_RSTMMTX, 1, 1); + avalon9_iic_xfer_pkg(avalon9, i, &send_pkg, NULL); + if (info->error_polling_cnt[i] >= 10) + detach_module(avalon9, i); + } + + if (ret == AVA9_SEND_OK && !decode_err) { + info->error_polling_cnt[i] = 0; + + if ((ar.opt == AVA9_P_STATUS) && + (info->mm_dna[i][AVA9_MM_DNA_LEN - 1] != ar.opt)) { + applog(LOG_ERR, "%s-%d-%d: Dup address found %d-%d", + avalon9->drv->name, avalon9->device_id, i, + info->mm_dna[i][AVA9_MM_DNA_LEN - 1], ar.opt); + hexdump((uint8_t *)&ar, sizeof(ar)); + detach_module(avalon9, i); + } + } + } + + return 0; +} + +static void copy_pool_stratum(struct pool *pool_stratum, struct pool *pool) +{ + int i; + int merkles = pool->merkles, job_id_len; + size_t coinbase_len = pool->coinbase_len; + unsigned short crc; + + if (!pool->swork.job_id) + return; + + if (pool_stratum->swork.job_id) { + job_id_len = strlen(pool->swork.job_id); + crc = crc16((unsigned char *)pool->swork.job_id, job_id_len); + job_id_len = strlen(pool_stratum->swork.job_id); + + if (crc16((unsigned char *)pool_stratum->swork.job_id, job_id_len) == crc) + return; + } + + cg_wlock(&pool_stratum->data_lock); + free(pool_stratum->swork.job_id); + free(pool_stratum->nonce1); + free(pool_stratum->coinbase); + + pool_stratum->coinbase = cgcalloc(coinbase_len, 1); + memcpy(pool_stratum->coinbase, pool->coinbase, coinbase_len); + + for (i = 0; i < pool_stratum->merkles; i++) + free(pool_stratum->swork.merkle_bin[i]); + if (merkles) { + pool_stratum->swork.merkle_bin = cgrealloc(pool_stratum->swork.merkle_bin, + sizeof(char *) * merkles + 1); + for (i = 0; i < merkles; i++) { + pool_stratum->swork.merkle_bin[i] = cgmalloc(32); + memcpy(pool_stratum->swork.merkle_bin[i], pool->swork.merkle_bin[i], 32); + } + } + + pool_stratum->sdiff = pool->sdiff; + pool_stratum->coinbase_len = pool->coinbase_len; + pool_stratum->nonce2_offset = pool->nonce2_offset; + pool_stratum->n2size = pool->n2size; + pool_stratum->merkles = pool->merkles; + pool_stratum->swork.job_id = strdup(pool->swork.job_id); + pool_stratum->nonce1 = strdup(pool->nonce1); + + memcpy(pool_stratum->ntime, pool->ntime, sizeof(pool_stratum->ntime)); + memcpy(pool_stratum->header_bin, pool->header_bin, sizeof(pool_stratum->header_bin)); + cg_wunlock(&pool_stratum->data_lock); +} + +static void avalon9_init_setting(struct cgpu_info *avalon9, int addr) +{ + struct avalon9_pkg send_pkg; + uint32_t tmp; + + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + + tmp = be32toh(opt_avalon9_freq_sel); + memcpy(send_pkg.data + 4, &tmp, 4); + + /* + * set flags: + * 0: ss switch + * 1: nonce check + * 2: roll enable + */ + tmp = 1; + if (!opt_avalon9_smart_speed) + tmp = 0; + tmp |= (opt_avalon9_nonce_check << 1); + tmp |= (opt_avalon9_roll_enable << 2); + send_pkg.data[8] = tmp & 0xff; + send_pkg.data[9] = opt_avalon9_nonce_mask & 0xff; + + tmp = be32toh(opt_avalon9_mux_l2h); + memcpy(send_pkg.data + 10, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set mux l2h %u", + avalon9->drv->name, avalon9->device_id, addr, + opt_avalon9_mux_l2h); + + tmp = be32toh(opt_avalon9_mux_h2l); + memcpy(send_pkg.data + 14, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set mux h2l %u", + avalon9->drv->name, avalon9->device_id, addr, + opt_avalon9_mux_h2l); + + tmp = be32toh(opt_avalon9_h2ltime0_spd); + memcpy(send_pkg.data + 18, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set h2ltime0 spd %u", + avalon9->drv->name, avalon9->device_id, addr, + opt_avalon9_h2ltime0_spd); + + tmp = be32toh(opt_avalon9_spdlow); + memcpy(send_pkg.data + 22, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set spdlow %u", + avalon9->drv->name, avalon9->device_id, addr, + opt_avalon9_spdlow); + + tmp = be32toh(opt_avalon9_spdhigh); + memcpy(send_pkg.data + 26, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set spdhigh %u", + avalon9->drv->name, avalon9->device_id, addr, + opt_avalon9_spdhigh); + + /* Package the data */ + avalon9_init_pkg(&send_pkg, AVA9_P_SET, 1, 1); + if (addr == AVA9_MODULE_BROADCAST) + avalon9_send_bc_pkgs(avalon9, &send_pkg); + else + avalon9_iic_xfer_pkg(avalon9, addr, &send_pkg, NULL); +} + +static void avalon9_set_voltage_level(struct cgpu_info *avalon9, int addr, unsigned int voltage[]) +{ + struct avalon9_info *info = avalon9->device_data; + struct avalon9_pkg send_pkg; + uint32_t tmp; + uint8_t i; + + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + + /* NOTE: miner_count should <= 8 */ + for (i = 0; i < info->miner_count[addr]; i++) { + tmp = be32toh(encode_voltage(voltage[i] + + opt_avalon9_voltage_level_offset)); + memcpy(send_pkg.data + i * 4, &tmp, 4); + } + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set voltage miner %d, (%d-%d)", + avalon9->drv->name, avalon9->device_id, addr, + i, voltage[0], voltage[info->miner_count[addr] - 1]); + + /* Package the data */ + avalon9_init_pkg(&send_pkg, AVA9_P_SET_VOLT, 1, 1); + if (addr == AVA9_MODULE_BROADCAST) + avalon9_send_bc_pkgs(avalon9, &send_pkg); + else + avalon9_iic_xfer_pkg(avalon9, addr, &send_pkg, NULL); +} + +static void avalon9_set_freq(struct cgpu_info *avalon9, int addr, int miner_id, unsigned int freq[]) +{ + struct avalon9_info *info = avalon9->device_data; + struct avalon9_pkg send_pkg; + uint32_t tmp, f; + uint8_t i; + + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + for (i = 0; i < AVA9_DEFAULT_PLL_CNT; i++) { + tmp = be32toh(api_get_cpm(freq[i])); + memcpy(send_pkg.data + i * 4, &tmp, 4); + } + + f = freq[0]; + for (i = 1; i < AVA9_DEFAULT_PLL_CNT; i++) + f = f > freq[i] ? f : freq[i]; + + f = f ? f : 1; + + tmp = AVA9_ASIC_TIMEOUT_CONST / f * 83 / 100; + tmp = be32toh(tmp); + memcpy(send_pkg.data + AVA9_DEFAULT_PLL_CNT * 4, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set freq miner %x-%x", + avalon9->drv->name, avalon9->device_id, addr, + miner_id, be32toh(tmp)); + + /* Package the data */ + avalon9_init_pkg(&send_pkg, AVA9_P_SET_PLL, miner_id + 1, info->miner_count[addr]); + + if (addr == AVA9_MODULE_BROADCAST) + avalon9_send_bc_pkgs(avalon9, &send_pkg); + else + avalon9_iic_xfer_pkg(avalon9, addr, &send_pkg, NULL); +} + +static void avalon9_set_factory_info(struct cgpu_info *avalon9, int addr, uint8_t value[]) +{ + struct avalon9_pkg send_pkg; + uint8_t i; + + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + + for (i = 0; i < AVA9_DEFAULT_FACTORY_INFO_CNT; i++) + send_pkg.data[i] = value[i]; + + /* Package the data */ + avalon9_init_pkg(&send_pkg, AVA9_P_SET_FAC, 1, 1); + if (addr == AVA9_MODULE_BROADCAST) + avalon9_send_bc_pkgs(avalon9, &send_pkg); + else + avalon9_iic_xfer_pkg(avalon9, addr, &send_pkg, NULL); +} + +static void avalon9_set_overclocking_info(struct cgpu_info *avalon9, int addr, uint8_t value[]) +{ + struct avalon9_pkg send_pkg; + uint8_t i; + + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + + for (i = 0; i < AVA9_DEFAULT_OVERCLOCKING_CNT; i++) + send_pkg.data[i] = value[i]; + + /* Package the data */ + avalon9_init_pkg(&send_pkg, AVA9_P_SET_OC, 1, 1); + if (addr == AVA9_MODULE_BROADCAST) + avalon9_send_bc_pkgs(avalon9, &send_pkg); + else + avalon9_iic_xfer_pkg(avalon9, addr, &send_pkg, NULL); +} + +static void avalon9_set_ss_param(struct cgpu_info *avalon9, int addr) +{ + struct avalon9_pkg send_pkg; + uint32_t tmp; + + if (!opt_avalon9_smart_speed) + return; + + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + + tmp = (opt_avalon9_th_pass << 16) | opt_avalon9_th_fail; + tmp = be32toh(tmp); + memcpy(send_pkg.data, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set th pass %u", + avalon9->drv->name, avalon9->device_id, addr, + opt_avalon9_th_pass); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set th fail %u", + avalon9->drv->name, avalon9->device_id, addr, + opt_avalon9_th_fail); + + tmp = ((opt_avalon9_th_add & 0x1) << 31) | ((opt_avalon9_th_mssel & 0x1) << 30) + | ((opt_avalon9_th_ms & 0x3fff) << 16) + | (opt_avalon9_th_init & 0xffff); + tmp = be32toh(tmp); + memcpy(send_pkg.data + 4, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set th add %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_th_add & 0x1)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set th mssel %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_th_mssel & 0x1)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set th ms %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_th_ms & 0x3fff)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set th init %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_th_init & 0xffff)); + + tmp = be32toh(opt_avalon9_th_timeout); + memcpy(send_pkg.data + 8, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set th timeout %u", + avalon9->drv->name, avalon9->device_id, addr, + opt_avalon9_th_timeout); + + tmp = ((opt_avalon9_lv3_th_add & 0x1) << 31) | ((opt_avalon9_lv2_th_add & 0x1) << 15) + | ((opt_avalon9_lv3_th_ms & 0x7fff) << 16) + | (opt_avalon9_lv2_th_ms & 0x7fff); + tmp = be32toh(tmp); + memcpy(send_pkg.data + 12, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv3 th add %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv3_th_add & 0x1)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv3 th ms %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv3_th_ms & 0x7fff)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv2 th add %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv2_th_add & 0x1)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv2 th ms %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv2_th_ms & 0x7fff)); + + tmp = ((opt_avalon9_lv5_th_add & 0x1) << 31) | ((opt_avalon9_lv4_th_add & 0x1) << 15) + | ((opt_avalon9_lv5_th_ms & 0x7fff) << 16) + | (opt_avalon9_lv4_th_ms & 0x7fff); + tmp = be32toh(tmp); + memcpy(send_pkg.data + 16, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv5 th add %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv5_th_add & 0x1)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv5 th ms %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv5_th_ms & 0x7fff)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv4 th add %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv4_th_add & 0x1)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv4 th ms %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv4_th_ms & 0x7fff)); + + tmp = ((opt_avalon9_lv7_th_add & 0x1) << 31) | ((opt_avalon9_lv6_th_add & 0x1) << 15) + | ((opt_avalon9_lv7_th_ms & 0x7fff) << 16) + | (opt_avalon9_lv6_th_ms & 0x7fff); + tmp = be32toh(tmp); + memcpy(send_pkg.data + 20, &tmp, 4); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv7 th add %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv7_th_add & 0x1)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv7 th ms %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv7_th_ms & 0x7fff)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv6 th add %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv6_th_add & 0x1)); + applog(LOG_DEBUG, "%s-%d-%d: avalon9 set lv6 th ms %u", + avalon9->drv->name, avalon9->device_id, addr, + (opt_avalon9_lv6_th_ms & 0x7fff)); + + /* Package the data */ + avalon9_init_pkg(&send_pkg, AVA9_P_SET_SS, 1, 1); + + if (addr == AVA9_MODULE_BROADCAST) + avalon9_send_bc_pkgs(avalon9, &send_pkg); + else + avalon9_iic_xfer_pkg(avalon9, addr, &send_pkg, NULL); +} + +static void avalon9_stratum_finish(struct cgpu_info *avalon9) +{ + struct avalon9_pkg send_pkg; + + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + avalon9_init_pkg(&send_pkg, AVA9_P_JOB_FIN, 1, 1); + avalon9_send_bc_pkgs(avalon9, &send_pkg); +} + +static void avalon9_set_finish(struct cgpu_info *avalon9, int addr) +{ + struct avalon9_pkg send_pkg; + + memset(send_pkg.data, 0, AVA9_P_DATA_LEN); + avalon9_init_pkg(&send_pkg, AVA9_P_SET_FIN, 1, 1); + avalon9_iic_xfer_pkg(avalon9, addr, &send_pkg, NULL); +} + +static void avalon9_sswork_update(struct cgpu_info *avalon9) +{ + struct avalon9_info *info = avalon9->device_data; + struct thr_info *thr = avalon9->thr[0]; + struct pool *pool; + int coinbase_len_posthash, coinbase_len_prehash; + + cgtime(&info->last_stratum); + /* + * NOTE: We need mark work_restart to private information, + * So that it cann't reset by hash_driver_work + */ + if (thr->work_restart) + info->work_restart = thr->work_restart; + applog(LOG_NOTICE, "%s-%d: New stratum: restart: %d, update: %d, clean: %d", + avalon9->drv->name, avalon9->device_id, + thr->work_restart, thr->work_update, thr->clean_jobs); + + /* Step 1: MM protocol check */ + pool = current_pool(); + if (!pool->has_stratum) + quit(1, "%s-%d: MM has to use stratum pools", avalon9->drv->name, avalon9->device_id); + + coinbase_len_prehash = pool->nonce2_offset - (pool->nonce2_offset % SHA256_BLOCK_SIZE); + coinbase_len_posthash = pool->coinbase_len - coinbase_len_prehash; + + if (coinbase_len_posthash + SHA256_BLOCK_SIZE > AVA9_P_COINBASE_SIZE) { + applog(LOG_ERR, "%s-%d: MM pool modified coinbase length(%d) is more than %d", + avalon9->drv->name, avalon9->device_id, + coinbase_len_posthash + SHA256_BLOCK_SIZE, AVA9_P_COINBASE_SIZE); + return; + } + if (pool->merkles > AVA9_P_MERKLES_COUNT) { + applog(LOG_ERR, "%s-%d: MM merkles has to be less then %d", avalon9->drv->name, avalon9->device_id, AVA9_P_MERKLES_COUNT); + return; + } + if (pool->n2size < 3) { + applog(LOG_ERR, "%s-%d: MM nonce2 size has to be >= 3 (%d)", avalon9->drv->name, avalon9->device_id, pool->n2size); + return; + } + cg_wlock(&info->update_lock); + + /* Step 2: Send out stratum pkgs */ + cg_rlock(&pool->data_lock); + info->pool_no = pool->pool_no; + copy_pool_stratum(&info->pool2, &info->pool1); + copy_pool_stratum(&info->pool1, &info->pool0); + copy_pool_stratum(&info->pool0, pool); + + avalon9_stratum_pkgs(avalon9, pool); + cg_runlock(&pool->data_lock); + + /* Step 3: Send out finish pkg */ + avalon9_stratum_finish(avalon9); + cg_wunlock(&info->update_lock); +} + +static int64_t avalon9_scanhash(struct thr_info *thr) +{ + struct cgpu_info *avalon9 = thr->cgpu; + struct avalon9_info *info = avalon9->device_data; + struct timeval current; + int i, j, k, count = 0; + int temp_max; + int64_t ret; + bool update_settings = false; + + if ((info->connecter == AVA9_CONNECTER_AUC) && + (unlikely(avalon9->usbinfo.nodev))) { + applog(LOG_ERR, "%s-%d: Device disappeared, shutting down thread", + avalon9->drv->name, avalon9->device_id); + return -1; + } + + /* Step 1: Stop polling and detach the device if there is no stratum in 3 minutes, network is down */ + cgtime(¤t); + if (tdiff(¤t, &(info->last_stratum)) > 180.0) { + for (i = 1; i < AVA9_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + detach_module(avalon9, i); + } + info->mm_count = 0; + return 0; + } + + /* Step 2: Try to detect new modules */ + if ((tdiff(¤t, &(info->last_detect)) > AVA9_MODULE_DETECT_INTERVAL) || + !info->mm_count) { + cgtime(&info->last_detect); + detect_modules(avalon9); + } + + /* Step 3: ASIC configrations (voltage and frequency) */ + for (i = 1; i < AVA9_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + update_settings = false; + + /* Check temperautre */ + temp_max = get_temp_max(info, i); + + /* Enter too hot */ + if (temp_max >= info->temp_overheat[i]) + info->cutoff[i] = 1; + + /* Exit too hot */ + if (info->cutoff[i] && (temp_max <= (info->temp_overheat[i] - 10))) + info->cutoff[i] = 0; + + switch (info->freq_mode[i]) { + case AVA9_FREQ_INIT_MODE: + update_settings = true; + for (j = 0; j < info->miner_count[i]; j++) { + for (k = 0; k < AVA9_DEFAULT_PLL_CNT; k++) { + if (opt_avalon9_freq[k] != AVA9_DEFAULT_FREQUENCY_IGNORE) + info->set_frequency[i][j][k] = opt_avalon9_freq[k]; + } + } + + avalon9_init_setting(avalon9, i); + + info->freq_mode[i] = AVA9_FREQ_PLLADJ_MODE; + break; + case AVA9_FREQ_PLLADJ_MODE: + if (opt_avalon9_smart_speed == AVA9_DEFAULT_SMARTSPEED_OFF) + break; + + /* AVA9_DEFAULT_SMARTSPEED_MODE1: auto speed by A3206 chips */ + break; + default: + applog(LOG_ERR, "%s-%d-%d: Invalid frequency mode %d", + avalon9->drv->name, avalon9->device_id, i, info->freq_mode[i]); + break; + } + if (update_settings) { + cg_wlock(&info->update_lock); + avalon9_set_voltage_level(avalon9, i, info->set_voltage_level[i]); + for (j = 0; j < info->miner_count[i]; j++) + avalon9_set_freq(avalon9, i, j, info->set_frequency[i][j]); + + if (opt_avalon9_smart_speed) + avalon9_set_ss_param(avalon9, i); + + avalon9_set_finish(avalon9, i); + cg_wunlock(&info->update_lock); + } + } + + /* Step 4: Polling */ + cg_rlock(&info->update_lock); + polling(avalon9); + cg_runlock(&info->update_lock); + + /* Step 5: Calculate mm count */ + for (i = 1; i < AVA9_DEFAULT_MODULARS; i++) { + if (info->enable[i]) + count++; + } + info->mm_count = count; + + /* Step 6: Calculate hashes. Use the diff1 value which is scaled by + * device diff and is usually lower than pool diff which will give a + * more stable result, but remove diff rejected shares to more closely + * approximate diff accepted values. */ + info->pending_diff1 += avalon9->diff1 - info->last_diff1; + info->last_diff1 = avalon9->diff1; + info->pending_diff1 -= avalon9->diff_rejected - info->last_rej; + info->last_rej = avalon9->diff_rejected; + if (info->pending_diff1 && !info->firsthash.tv_sec) { + cgtime(&info->firsthash); + copy_time(&(avalon9->dev_start_tv), &(info->firsthash)); + } + + if (info->pending_diff1 <= 0) + ret = 0; + else { + ret = info->pending_diff1; + info->pending_diff1 = 0; + } + return ret * 0xffffffffull; +} + +static float avalon9_hash_cal(struct cgpu_info *avalon9, int modular_id) +{ + struct avalon9_info *info = avalon9->device_data; + uint32_t tmp_freq[AVA9_DEFAULT_PLL_CNT]; + unsigned int i, j, k; + float mhsmm; + + mhsmm = 0; + for (i = 0; i < info->miner_count[modular_id]; i++) { + for (j = 0; j < info->asic_count[modular_id]; j++) { + for (k = 0; k < AVA9_DEFAULT_PLL_CNT; k++) + mhsmm += (info->get_asic[modular_id][i][j][2 + k] * info->get_frequency[modular_id][i][k]); + } + } + + return mhsmm; +} + +#define STATBUFLEN_WITHOUT_DBG (6 * 1024) +#define STATBUFLEN_WITH_DBG (6 * 7 * 1024) +static struct api_data *avalon9_api_stats(struct cgpu_info *avalon9) +{ + struct api_data *root = NULL; + struct avalon9_info *info = avalon9->device_data; + int i, j, k, m; + double a, b, dh; + char buf[256]; + char *statbuf = NULL; + struct timeval current; + float mhsmm, auc_temp = 0.0; + double sum; + int avg, cnt, max_vl, max_id, min_vl, min_id; + + cgtime(¤t); + if (opt_debug) + statbuf = cgcalloc(STATBUFLEN_WITH_DBG, 1); + else + statbuf = cgcalloc(STATBUFLEN_WITHOUT_DBG, 1); + + for (i = 1; i < AVA9_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + sprintf(buf, "Ver[%s]", info->mm_version[i]); + strcpy(statbuf, buf); + + sprintf(buf, " DNA[%02x%02x%02x%02x%02x%02x%02x%02x]", + info->mm_dna[i][0], + info->mm_dna[i][1], + info->mm_dna[i][2], + info->mm_dna[i][3], + info->mm_dna[i][4], + info->mm_dna[i][5], + info->mm_dna[i][6], + info->mm_dna[i][7]); + strcat(statbuf, buf); + + sprintf(buf, " Elapsed[%.0f]", tdiff(¤t, &(info->elapsed[i]))); + strcat(statbuf, buf); + + strcat(statbuf, " MW["); + info->local_works[i] = 0; + for (j = 0; j < info->miner_count[i]; j++) { + info->local_works[i] += info->local_works_i[i][j]; + sprintf(buf, "%"PRIu64" ", info->local_works_i[i][j]); + strcat(statbuf, buf); + } + statbuf[strlen(statbuf) - 1] = ']'; + + sprintf(buf, " LW[%"PRIu64"]", info->local_works[i]); + strcat(statbuf, buf); + + strcat(statbuf, " MH["); + info->hw_works[i] = 0; + for (j = 0; j < info->miner_count[i]; j++) { + info->hw_works[i] += info->hw_works_i[i][j]; + sprintf(buf, "%"PRIu64" ", info->hw_works_i[i][j]); + strcat(statbuf, buf); + } + statbuf[strlen(statbuf) - 1] = ']'; + + sprintf(buf, " HW[%"PRIu64"]", info->hw_works[i]); + strcat(statbuf, buf); + + { + double a, b, dh; + + a = 0; + b = 0; + for (j = 0; j < info->miner_count[i]; j++) { + for (k = 0; k < info->asic_count[i]; k++) { + a += info->get_asic[i][j][k][0]; + b += info->get_asic[i][j][k][1]; + } + } + dh = b ? (b / (a + b)) * 100 : 0; + sprintf(buf, " DH[%.3f%%]", dh); + strcat(statbuf, buf); + } + + sprintf(buf, " Temp[%d]", info->temp_mm[i]); + strcat(statbuf, buf); + + sprintf(buf, " TMax[%d]", get_temp_max(info, i)); + strcat(statbuf, buf); + + sprintf(buf, " Fan[%d]", info->fan_cpm[i]); + strcat(statbuf, buf); + + sprintf(buf, " FanR[%d%%]", info->fan_pct[i]); + strcat(statbuf, buf); + + sprintf(buf, " Vi["); + strcat(statbuf, buf); + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, "%d ", info->get_vin[i][j]); + strcat(statbuf, buf); + } + statbuf[strlen(statbuf) - 1] = ']'; + + sprintf(buf, " Vo["); + strcat(statbuf, buf); + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, "%d ", info->get_voltage[i][j]); + strcat(statbuf, buf); + } + statbuf[strlen(statbuf) - 1] = ']'; + + if (opt_debug) { + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, " PLL%d[", j); + strcat(statbuf, buf); + for (k = 0; k < AVA9_DEFAULT_PLL_CNT; k++) { + sprintf(buf, "%d ", info->get_pll[i][j][k]); + strcat(statbuf, buf); + } + statbuf[strlen(statbuf) - 1] = ']'; + } + } + + mhsmm = avalon9_hash_cal(avalon9, i); + sprintf(buf, " GHSmm[%.2f] WU[%.2f] Freq[%.2f]", (float)mhsmm / 1000, + info->diff1[i] / tdiff(¤t, &(info->elapsed[i])) * 60.0, + (float)mhsmm / (info->asic_count[i] * info->miner_count[i] * 256)); + strcat(statbuf, buf); + + sprintf(buf, " PG[%d]", info->power_good[i]); + strcat(statbuf, buf); + + sprintf(buf, " Led[%d]", info->led_indicator[i]); + strcat(statbuf, buf); + + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, " MW%d[", j); + strcat(statbuf, buf); + for (k = 0; k < info->asic_count[i]; k++) { + sprintf(buf, "%"PRIu64" ", info->chip_matching_work[i][j][k]); + strcat(statbuf, buf); + } + + statbuf[strlen(statbuf) - 1] = ']'; + } + + sprintf(buf, " TA[%d]", info->total_asics[i]); + strcat(statbuf, buf); + + strcat(statbuf, " ECHU["); + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, "%d ", info->error_code[i][j]); + strcat(statbuf, buf); + } + statbuf[strlen(statbuf) - 1] = ']'; + + sprintf(buf, " ECMM[%d]", info->error_code[i][j]); + strcat(statbuf, buf); + + if (opt_debug) { + sprintf(buf, " FAC0[%d]", info->factory_info[0]); + strcat(statbuf, buf); + + sprintf(buf, " OC[%d]", info->overclocking_info[0]); + strcat(statbuf, buf); + + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, " SF%d[", j); + strcat(statbuf, buf); + for (k = 0; k < AVA9_DEFAULT_PLL_CNT; k++) { + sprintf(buf, "%d ", info->get_frequency[i][j][k]); + strcat(statbuf, buf); + } + + statbuf[strlen(statbuf) - 1] = ']'; + } + + strcat(statbuf, " PMUV["); + for (j = 0; j < AVA9_DEFAULT_PMU_CNT; j++) { + sprintf(buf, "%s ", info->pmu_version[i][j]); + strcat(statbuf, buf); + } + statbuf[strlen(statbuf) - 1] = ']'; + + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, " PVT_T%d[", j); + strcat(statbuf, buf); + for (k = 0; k < info->asic_count[i]; k++) { + sprintf(buf, "%3d ", info->temp[i][j][k]); + strcat(statbuf, buf); + } + + statbuf[strlen(statbuf) - 1] = ']'; + statbuf[strlen(statbuf)] = '\0'; + } + + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, " PVT_V%d[", j); + strcat(statbuf, buf); + for (k = 0; k < info->asic_count[i]; k++) { + sprintf(buf, "%d ", info->core_volt[i][j][k]); + strcat(statbuf, buf); + } + + statbuf[strlen(statbuf) - 1] = ']'; + statbuf[strlen(statbuf)] = '\0'; + } + + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, " ERATIO%d[", j); + strcat(statbuf, buf); + for (k = 0; k < info->asic_count[i]; k++) { + if (info->get_asic[i][j][k][0]) + sprintf(buf, "%6.2f%% ", (double)(info->get_asic[i][j][k][1] * 100.0 / (info->get_asic[i][j][k][0] + info->get_asic[i][j][k][1]))); + else + sprintf(buf, "%6.2f%% ", 0.0); + strcat(statbuf, buf); + } + + statbuf[strlen(statbuf) - 1] = ']'; + } + + int l; + /* i: modular, j: miner, k:asic, l:value */ + for (l = 0; l < 2; l++) { + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, " C_%02d_%02d[", j, l); + strcat(statbuf, buf); + for (k = 0; k < info->asic_count[i]; k++) { + sprintf(buf, "%7d ", info->get_asic[i][j][k][l]); + strcat(statbuf, buf); + } + + statbuf[strlen(statbuf) - 1] = ']'; + } + } + + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, " GHSmm%02d[", j); + strcat(statbuf, buf); + for (k = 0; k < info->asic_count[i]; k++) { + mhsmm = 0; + for (l = 2; l < (2 + AVA9_DEFAULT_PLL_CNT); l++) { + mhsmm += (info->get_asic[i][j][k][l] * info->get_frequency[i][j][l - 2]); + } + + sprintf(buf, "%7.2f ", mhsmm / 1000); + strcat(statbuf, buf); + } + statbuf[strlen(statbuf) - 1] = ']'; + } + } + + sprintf(buf, " FM[%d]", info->freq_mode[i]); + strcat(statbuf, buf); + + strcat(statbuf, " CRC["); + for (j = 0; j < info->miner_count[i]; j++) { + sprintf(buf, "%d ", info->error_crc[i][j]); + strcat(statbuf, buf); + } + statbuf[strlen(statbuf) - 1] = ']'; + + sprintf(buf, "MM ID%d", i); + root = api_add_string(root, buf, statbuf, true); + } + free(statbuf); + + root = api_add_int(root, "MM Count", &(info->mm_count), true); + root = api_add_int(root, "Smart Speed", &opt_avalon9_smart_speed, true); + if (info->connecter == AVA9_CONNECTER_IIC) + root = api_add_string(root, "Connecter", "IIC", true); + + if (info->connecter == AVA9_CONNECTER_AUC) { + root = api_add_string(root, "Connecter", "AUC", true); + root = api_add_string(root, "AUC VER", info->auc_version, false); + root = api_add_int(root, "AUC I2C Speed", &(info->auc_speed), true); + root = api_add_int(root, "AUC I2C XDelay", &(info->auc_xdelay), true); + root = api_add_int(root, "AUC Sensor", &(info->auc_sensor), true); + auc_temp = decode_auc_temp(info->auc_sensor); + root = api_add_temp(root, "AUC Temperature", &auc_temp, true); + } + + root = api_add_bool(root, "Connection Overloaded", &info->conn_overloaded, true); + root = api_add_int(root, "Voltage Level Offset", &opt_avalon9_voltage_level_offset, true); + root = api_add_uint32(root, "Nonce Mask", &opt_avalon9_nonce_mask, true); + + return root; +} + +/* format: voltage[-addr[-miner]] + * addr[0, AVA9_DEFAULT_MODULARS - 1], 0 means all modulars + * miner[0, miner_count], 0 means all miners + */ +char *set_avalon9_device_voltage_level(struct cgpu_info *avalon9, char *arg) +{ + struct avalon9_info *info = avalon9->device_data; + int val; + unsigned int addr = 0, i, j; + uint32_t miner_id = 0; + + if (!(*arg)) + return NULL; + + sscanf(arg, "%d-%d-%d", &val, &addr, &miner_id); + + if (val < AVA9_DEFAULT_VOLTAGE_LEVEL_MIN || val > AVA9_DEFAULT_VOLTAGE_LEVEL_MAX) + return "Invalid value passed to set_avalon9_device_voltage_level"; + + if (addr >= AVA9_DEFAULT_MODULARS) { + applog(LOG_ERR, "invalid modular index: %d, valid range 0-%d", addr, (AVA9_DEFAULT_MODULARS - 1)); + return "Invalid modular index to set_avalon9_device_voltage_level"; + } + + if (!addr) { + for (i = 1; i < AVA9_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + if (miner_id > info->miner_count[i]) { + applog(LOG_ERR, "invalid miner index: %d, valid range 0-%d", miner_id, info->miner_count[i]); + return "Invalid miner index to set_avalon9_device_voltage_level"; + } + + if (miner_id) + info->set_voltage_level[i][miner_id - 1] = val; + else { + for (j = 0; j < info->miner_count[i]; j++) + info->set_voltage_level[i][j] = val; + } + avalon9_set_voltage_level(avalon9, i, info->set_voltage_level[i]); + } + } else { + if (!info->enable[addr]) { + applog(LOG_ERR, "Disabled modular:%d", addr); + return "Disabled modular to set_avalon9_device_voltage_level"; + } + + if (miner_id > info->miner_count[addr]) { + applog(LOG_ERR, "invalid miner index: %d, valid range 0-%d", miner_id, info->miner_count[addr]); + return "Invalid miner index to set_avalon9_device_voltage_level"; + } + + if (miner_id) + info->set_voltage_level[addr][miner_id - 1] = val; + else { + for (j = 0; j < info->miner_count[addr]; j++) + info->set_voltage_level[addr][j] = val; + } + avalon9_set_voltage_level(avalon9, addr, info->set_voltage_level[addr]); + } + + applog(LOG_NOTICE, "%s-%d: Update voltage-level to %d", avalon9->drv->name, avalon9->device_id, val); + + return NULL; +} + +/* + * format: freq[-addr[-miner]] + * addr[0, AVA9_DEFAULT_MODULARS - 1], 0 means all modulars + * miner[0, miner_count], 0 means all miners + */ +char *set_avalon9_device_freq(struct cgpu_info *avalon9, char *arg) +{ + struct avalon9_info *info = avalon9->device_data; + unsigned int val, addr = 0, i, j, k; + uint32_t miner_id = 0; + + if (!(*arg)) + return NULL; + + sscanf(arg, "%d-%d-%d", &val, &addr, &miner_id); + + if (val > AVA9_DEFAULT_FREQUENCY_MAX) + return "Invalid value passed to set_avalon9_device_freq"; + + if (addr >= AVA9_DEFAULT_MODULARS) { + applog(LOG_ERR, "invalid modular index: %d, valid range 0-%d", addr, (AVA9_DEFAULT_MODULARS - 1)); + return "Invalid modular index to set_avalon9_device_freq"; + } + + if (!addr) { + for (i = 1; i < AVA9_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + if (miner_id > info->miner_count[i]) { + applog(LOG_ERR, "invalid miner index: %d, valid range 0-%d", miner_id, info->miner_count[i]); + return "Invalid miner index to set_avalon9_device_freq"; + } + + if (miner_id) { + for (k = 0; k < AVA9_DEFAULT_PLL_CNT; k++) + info->set_frequency[i][miner_id - 1][k] = val; + + avalon9_set_freq(avalon9, i, miner_id - 1, info->set_frequency[i][miner_id - 1]); + } else { + for (j = 0; j < info->miner_count[i]; j++) { + for (k = 0; k < AVA9_DEFAULT_PLL_CNT; k++) + info->set_frequency[i][j][k] = val; + + avalon9_set_freq(avalon9, i, j, info->set_frequency[i][j]); + } + } + } + } else { + if (!info->enable[addr]) { + applog(LOG_ERR, "Disabled modular:%d", addr); + return "Disabled modular to set_avalon9_device_freq"; + } + + if (miner_id > info->miner_count[addr]) { + applog(LOG_ERR, "invalid miner index: %d, valid range 0-%d", miner_id, info->miner_count[addr]); + return "Invalid miner index to set_avalon9_device_freq"; + } + + if (miner_id) { + for (k = 0; k < AVA9_DEFAULT_PLL_CNT; k++) + info->set_frequency[addr][miner_id - 1][k] = val; + + avalon9_set_freq(avalon9, addr, miner_id - 1, info->set_frequency[addr][miner_id - 1]); + + } else { + for (j = 0; j < info->miner_count[addr]; j++) { + for (k = 0; k < AVA9_DEFAULT_PLL_CNT; k++) + info->set_frequency[addr][j][k] = val; + + avalon9_set_freq(avalon9, addr, j, info->set_frequency[addr][j]); + } + } + } + + applog(LOG_NOTICE, "%s-%d: Update frequency to %d", + avalon9->drv->name, avalon9->device_id, val); + + return NULL; +} + +char *set_avalon9_factory_info(struct cgpu_info *avalon9, char *arg) +{ + struct avalon9_info *info = avalon9->device_data; + char type[AVA9_DEFAULT_FACTORY_INFO_1_CNT]; + int val; + + if (!(*arg)) + return NULL; + + memset(type, 0, AVA9_DEFAULT_FACTORY_INFO_1_CNT); + + sscanf(arg, "%d-%s", &val, type); + + if ((val != AVA9_DEFAULT_FACTORY_INFO_0_IGNORE) && + (val < AVA9_DEFAULT_FACTORY_INFO_0_MIN || val > AVA9_DEFAULT_FACTORY_INFO_0_MAX)) + return "Invalid value passed to set_avalon9_factory_info"; + + info->factory_info[0] = val; + + memcpy(&info->factory_info[1], type, AVA9_DEFAULT_FACTORY_INFO_1_CNT); + + avalon9_set_factory_info(avalon9, 0, (uint8_t *)info->factory_info); + + applog(LOG_NOTICE, "%s-%d: Update factory info %d", + avalon9->drv->name, avalon9->device_id, val); + + return NULL; +} + +char *set_avalon9_overclocking_info(struct cgpu_info *avalon9, char *arg) +{ + struct avalon9_info *info = avalon9->device_data; + int val; + + if (!(*arg)) + return NULL; + + sscanf(arg, "%d", &val); + + if (val != AVA9_DEFAULT_OVERCLOCKING_OFF && val != AVA9_DEFAULT_OVERCLOCKING_ON) + return "Invalid value passed to set_avalon9_overclocking_info"; + + info->overclocking_info[0] = val; + avalon9_set_overclocking_info(avalon9, 0, (uint8_t *)info->overclocking_info); + + applog(LOG_NOTICE, "%s-%d: Update Overclocking info %d", + avalon9->drv->name, avalon9->device_id, val); + + return NULL; +} + +static char *avalon9_set_device(struct cgpu_info *avalon9, char *option, char *setting, char *replybuf) +{ + unsigned int val; + struct avalon9_info *info = avalon9->device_data; + + if (strcasecmp(option, "help") == 0) { + sprintf(replybuf, "pdelay|fan|frequency|led|voltage"); + return replybuf; + } + + if (strcasecmp(option, "pdelay") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing polling delay setting"); + return replybuf; + } + + val = (unsigned int)atoi(setting); + if (val < 1 || val > 65535) { + sprintf(replybuf, "invalid polling delay: %d, valid range 1-65535", val); + return replybuf; + } + + opt_avalon9_polling_delay = val; + + applog(LOG_NOTICE, "%s-%d: Update polling delay to: %d", + avalon9->drv->name, avalon9->device_id, val); + + return NULL; + } + + if (strcasecmp(option, "fan") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing fan value"); + return replybuf; + } + + if (set_avalon9_fan(setting)) { + sprintf(replybuf, "invalid fan value, valid range 0-100"); + return replybuf; + } + + applog(LOG_NOTICE, "%s-%d: Update fan to %d-%d", + avalon9->drv->name, avalon9->device_id, + opt_avalon9_fan_min, opt_avalon9_fan_max); + + return NULL; + } + + if (strcasecmp(option, "frequency") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing frequency value"); + return replybuf; + } + + return set_avalon9_device_freq(avalon9, setting); + } + + if (strcasecmp(option, "led") == 0) { + int val_led = -1; + + if (!setting || !*setting) { + sprintf(replybuf, "missing module_id setting"); + return replybuf; + } + + sscanf(setting, "%d-%d", &val, &val_led); + if (val < 1 || val >= AVA9_DEFAULT_MODULARS) { + sprintf(replybuf, "invalid module_id: %d, valid range 1-%d", val, AVA9_DEFAULT_MODULARS); + return replybuf; + } + + if (!info->enable[val]) { + sprintf(replybuf, "the current module was disabled %d", val); + return replybuf; + } + + if (val_led == -1) + info->led_indicator[val] = !info->led_indicator[val]; + else { + if (val_led < 0 || val_led > 1) { + sprintf(replybuf, "invalid LED status: %d, valid value 0|1", val_led); + return replybuf; + } + + if (val_led != info->led_indicator[val]) + info->led_indicator[val] = val_led; + } + + applog(LOG_NOTICE, "%s-%d: Module:%d, LED: %s", + avalon9->drv->name, avalon9->device_id, + val, info->led_indicator[val] ? "on" : "off"); + + return NULL; + } + + if (strcasecmp(option, "voltage-level") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing voltage-level value"); + return replybuf; + } + + return set_avalon9_device_voltage_level(avalon9, setting); + } + + if (strcasecmp(option, "factory") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing factory info"); + return replybuf; + } + + return set_avalon9_factory_info(avalon9, setting); + } + + if (strcasecmp(option, "reboot") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing reboot value"); + return replybuf; + } + + sscanf(setting, "%d", &val); + if (val < 1 || val >= AVA9_DEFAULT_MODULARS) { + sprintf(replybuf, "invalid module_id: %d, valid range 1-%d", val, AVA9_DEFAULT_MODULARS); + return replybuf; + } + + info->reboot[val] = true; + + return NULL; + } + + if (strcasecmp(option, "overclocking") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing overclocking info"); + return replybuf; + } + + return set_avalon9_overclocking_info(avalon9, setting); + } + + sprintf(replybuf, "Unknown option: %s", option); + return replybuf; +} + +static void avalon9_statline_before(char *buf, size_t bufsiz, struct cgpu_info *avalon9) +{ + struct avalon9_info *info = avalon9->device_data; + int temp = -273; + int fanmin = AVA9_DEFAULT_FAN_MAX; + int i, j, k; + uint32_t frequency = 0; + float ghs_sum = 0, mhsmm = 0; + double pass_num = 0.0, fail_num = 0.0; + + for (i = 1; i < AVA9_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + if (fanmin >= info->fan_pct[i]) + fanmin = info->fan_pct[i]; + + if (temp < get_temp_max(info, i)) + temp = get_temp_max(info, i); + + mhsmm = avalon9_hash_cal(avalon9, i); + frequency += (mhsmm / (info->asic_count[i] * info->miner_count[i] * 256)); + ghs_sum += (mhsmm / 1000); + + for (j = 0; j < info->miner_count[i]; j++) { + for (k = 0; k < info->asic_count[i]; k++) { + pass_num += info->get_asic[i][j][k][0]; + fail_num += info->get_asic[i][j][k][1]; + } + } + } + + if (info->mm_count) + frequency /= info->mm_count; + + tailsprintf(buf, bufsiz, "%4dMhz %.2fGHS %2dC %.2f%% %3d%%", frequency, ghs_sum, temp, + (fail_num + pass_num) ? fail_num * 100.0 / (fail_num + pass_num) : 0, fanmin); +} + +struct device_drv avalon9_drv = { + .drv_id = DRIVER_avalon9, + .dname = "avalon9", + .name = "AV9", + .set_device = avalon9_set_device, + .get_api_stats = avalon9_api_stats, + .get_statline_before = avalon9_statline_before, + .drv_detect = avalon9_detect, + .thread_prepare = avalon9_prepare, + .hash_work = hash_driver_work, + .flush_work = avalon9_sswork_update, + .update_work = avalon9_sswork_update, + .scanwork = avalon9_scanhash, + .max_diff = AVA9_DRV_DIFFMAX, + .genwork = true, +}; diff --git a/driver-avalon9.h b/driver-avalon9.h new file mode 100644 index 0000000000..a60afffe3f --- /dev/null +++ b/driver-avalon9.h @@ -0,0 +1,366 @@ +/* + * Copyright 2016-2017 Mikeqin + * Copyright 2016 Con Kolivas + * + * 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 _AVALON9_H_ +#define _AVALON9_H_ + +#include "util.h" +#include "i2c-context.h" + +#ifdef USE_AVALON9 + +#define AVA9_DEFAULT_FAN_MIN 5 /* % */ +#define AVA9_DEFAULT_FAN_MAX 100 +#define AVA9_DEFAULT_FAN_INTERVAL 15 /* Seconds */ + +#define AVA9_DEFAULT_TEMP_TARGET 97 +#define AVA9_DEFAULT_TEMP_OVERHEAT 105 +#define AVA9_DEFAULT_TEMP_HYSTERESIS 5 + +#define AVA9_DEFAULT_VOLTAGE_LEVEL_MIN -15 +#define AVA9_DEFAULT_VOLTAGE_LEVEL_MAX 15 +#define AVA9_INVALID_VOLTAGE_LEVEL -16 + +#define AVA9_DEFAULT_VOLTAGE_LEVEL_OFFSET_MIN -2 +#define AVA9_DEFAULT_VOLTAGE_LEVEL_OFFSET 0 +#define AVA9_DEFAULT_VOLTAGE_LEVEL_OFFSET_MAX 1 + +#define AVA9_DEFAULT_FACTORY_INFO_0_MIN -15 +#define AVA9_DEFAULT_FACTORY_INFO_0_MAX 15 +#define AVA9_DEFAULT_FACTORY_INFO_0_CNT 1 +#define AVA9_DEFAULT_FACTORY_INFO_0_IGNORE 16 + +#define AVA9_DEFAULT_FACTORY_INFO_1_CNT 3 + +#define AVA9_DEFAULT_OVERCLOCKING_OFF 0 +#define AVA9_DEFAULT_OVERCLOCKING_ON 1 + +#define AVA9_DEFAULT_FREQUENCY_0M 0 +#define AVA9_DEFAULT_FREQUENCY_650M 650 +#define AVA9_DEFAULT_FREQUENCY_725M 725 +#define AVA9_DEFAULT_FREQUENCY_750M 750 +#define AVA9_DEFAULT_FREQUENCY_775M 775 +#define AVA9_DEFAULT_FREQUENCY_800M 800 +#define AVA9_DEFAULT_FREQUENCY_850M 850 +#define AVA9_DEFAULT_FREQUENCY_MAX 1200 +#define AVA9_DEFAULT_FREQUENCY_IGNORE 1250 +#define AVA9_DEFAULT_FREQUENCY_SEL 7 + +#define AVA9_DEFAULT_MODULARS 7 /* Only support 6 modules maximum with one AUC */ +#define AVA9_DEFAULT_MINER_CNT 4 +#define AVA9_DEFAULT_ASIC_MAX 26 +#define AVA9_DEFAULT_PLL_CNT 7 +#define AVA9_DEFAULT_PMU_CNT 2 +#define AVA9_DEFAULT_CORE_VOLT_CNT 8 + +#define AVA9_DEFAULT_POLLING_DELAY 20 /* ms */ +#define AVA9_DEFAULT_NTIME_OFFSET 2 + +#define AVA9_DEFAULT_SMARTSPEED_OFF 0 +#define AVA9_DEFAULT_SMARTSPEED_MODE1 1 +#define AVA9_DEFAULT_SMART_SPEED (AVA9_DEFAULT_SMARTSPEED_MODE1) + +#define AVA9_DEFAULT_TH_PASS 600 +#define AVA9_DEFAULT_TH_FAIL 16000 +#define AVA9_DEFAULT_TH_INIT 16001 +#define AVA9_DEFAULT_TH_ADD 0 +#define AVA9_DEFAULT_TH_MS 10 +#define AVA9_DEFAULT_TH_MSSEL 0 +#define AVA9_DEFAULT_TH_TIMEOUT 55000 +#define AVA9_DEFAULT_NONCE_MASK 25 +#define AVA9_DEFAULT_NONCE_CHECK 1 +#define AVA9_DEFAULT_MUX_L2H 0 +#define AVA9_DEFAULT_MUX_H2L 1 +#define AVA9_DEFAULT_H2LTIME0_SPD 3 +#define AVA9_DEFAULT_ROLL_ENABLE 1 +#define AVA9_DEFAULT_SPDLOW 5 +#define AVA9_DEFAULT_SPDHIGH 7 + +#define AVA9_DEFAULT_LV2_TH_MS 0 +#define AVA9_DEFAULT_LV3_TH_MS 0 +#define AVA9_DEFAULT_LV4_TH_MS 0 +#define AVA9_DEFAULT_LV5_TH_MS 0 +#define AVA9_DEFAULT_LV6_TH_MS 0 +#define AVA9_DEFAULT_LV7_TH_MS 0 +#define AVA9_DEFAULT_LV2_TH_ADD 0 +#define AVA9_DEFAULT_LV3_TH_ADD 0 +#define AVA9_DEFAULT_LV4_TH_ADD 0 +#define AVA9_DEFAULT_LV5_TH_ADD 0 +#define AVA9_DEFAULT_LV6_TH_ADD 0 +#define AVA9_DEFAULT_LV7_TH_ADD 0 + +#define AVA9_DEFAULT_IIC_DETECT false + +#define AVA9_PWM_MAX 0x3FF +#define AVA9_DRV_DIFFMAX 1024 +#define AVA9_ASIC_TIMEOUT_CONST 419430400 /* (2^32 * 1000) / (256 * 40) */ + +#define AVA9_MODULE_DETECT_INTERVAL 30 /* 30 s */ + +#define AVA9_AUC_VER_LEN 12 /* Version length: 12 (AUC-YYYYMMDD) */ +#define AVA9_AUC_SPEED 400000 +#define AVA9_AUC_XDELAY 19200 /* 4800 = 1ms in AUC (11U14) */ +#define AVA9_AUC_P_SIZE 64 + +#define AVA9_CONNECTER_AUC 1 +#define AVA9_CONNECTER_IIC 2 + +/* avalon9 protocol package type from MM protocol.h */ +#define AVA9_MM_VER_LEN 15 +#define AVA9_MM_DNA_LEN 8 +#define AVA9_H1 'C' +#define AVA9_H2 'N' + +#define AVA9_P_COINBASE_SIZE (6 * 1024 + 64) +#define AVA9_P_MERKLES_COUNT 30 + +#define AVA9_P_COUNT 40 +#define AVA9_P_DATA_LEN 32 + +/* Broadcase with block iic_write*/ +#define AVA9_P_DETECT 0x10 + +/* Broadcase With non-block iic_write*/ +#define AVA9_P_STATIC 0x11 +#define AVA9_P_JOB_ID 0x12 +#define AVA9_P_COINBASE 0x13 +#define AVA9_P_MERKLES 0x14 +#define AVA9_P_HEADER 0x15 +#define AVA9_P_TARGET 0x16 +#define AVA9_P_JOB_FIN 0x17 + +/* Broadcase or with I2C address */ +#define AVA9_P_SET 0x20 +#define AVA9_P_SET_FIN 0x21 +#define AVA9_P_SET_VOLT 0x22 +#define AVA9_P_SET_PMU 0x24 +#define AVA9_P_SET_PLL 0x25 +#define AVA9_P_SET_SS 0x26 + +/* 0x27 reserved */ +#define AVA9_P_SET_FAC 0x28 +#define AVA9_P_SET_OC 0x29 + +/* Have to send with I2C address */ +#define AVA9_P_POLLING 0x30 +#define AVA9_P_SYNC 0x31 +#define AVA9_P_TEST 0x32 +#define AVA9_P_RSTMMTX 0x33 +#define AVA9_P_GET_VOLT 0x34 + +/* Back to host */ +#define AVA9_P_ACKDETECT 0x40 +#define AVA9_P_STATUS 0x41 +#define AVA9_P_NONCE 0x42 +#define AVA9_P_TEST_RET 0x43 +#define AVA9_P_STATUS_VOLT 0x46 +#define AVA9_P_STATUS_PMU 0x48 +#define AVA9_P_STATUS_PLL 0x49 +#define AVA9_P_STATUS_LOG 0x4a +#define AVA9_P_STATUS_ASIC 0x4b +#define AVA9_P_STATUS_PVT 0x4c +#define AVA9_P_STATUS_FAC 0x4d +#define AVA9_P_STATUS_OC 0x4e + +#define AVA9_MODULE_BROADCAST 0 +/* End of avalon9 protocol package type */ + +#define AVA9_IIC_RESET 0xa0 +#define AVA9_IIC_INIT 0xa1 +#define AVA9_IIC_DEINIT 0xa2 +#define AVA9_IIC_XFER 0xa5 +#define AVA9_IIC_INFO 0xa6 + +#define AVA9_FREQ_INIT_MODE 0x0 +#define AVA9_FREQ_PLLADJ_MODE 0x1 + +#define AVA9_DEFAULT_FACTORY_INFO_CNT (AVA9_DEFAULT_FACTORY_INFO_0_CNT + AVA9_DEFAULT_FACTORY_INFO_1_CNT) + +#define AVA9_DEFAULT_OVERCLOCKING_CNT 1 + +#define AVA9_MM921_VIN_ADC_RATIO (3.3 / 4095.0 * 25.62 / 5.62 * 1000.0 * 100.0) +#define AVA9_MM921_VOUT_ADC_RATIO (3.3 / 4095.0 * 72.3 / 20.0 * 10000.0 * 100.0) + +struct avalon9_pkg { + uint8_t head[2]; + uint8_t type; + uint8_t opt; + uint8_t idx; + uint8_t cnt; + uint8_t data[32]; + uint8_t crc[2]; +}; +#define avalon9_ret avalon9_pkg + +struct avalon9_info { + /* Public data */ + int64_t last_diff1; + int64_t pending_diff1; + double last_rej; + + int mm_count; + int xfer_err_cnt; + int pool_no; + + struct timeval firsthash; + struct timeval last_fan_adj; + struct timeval last_stratum; + struct timeval last_detect; + + cglock_t update_lock; + + struct pool pool0; + struct pool pool1; + struct pool pool2; + + bool work_restart; + + uint32_t last_jobid; + + /* For connecter */ + char auc_version[AVA9_AUC_VER_LEN + 1]; + + int auc_speed; + int auc_xdelay; + int auc_sensor; + + struct i2c_ctx *i2c_slaves[AVA9_DEFAULT_MODULARS]; + + uint8_t connecter; /* AUC or IIC */ + + /* For modulars */ + bool enable[AVA9_DEFAULT_MODULARS]; + bool reboot[AVA9_DEFAULT_MODULARS]; + + struct timeval elapsed[AVA9_DEFAULT_MODULARS]; + + uint8_t mm_dna[AVA9_DEFAULT_MODULARS][AVA9_MM_DNA_LEN]; + char mm_version[AVA9_DEFAULT_MODULARS][AVA9_MM_VER_LEN + 1]; /* It's a string */ + uint32_t total_asics[AVA9_DEFAULT_MODULARS]; + uint32_t max_ntime; /* Maximum: 7200 */ + + int mod_type[AVA9_DEFAULT_MODULARS]; + uint8_t miner_count[AVA9_DEFAULT_MODULARS]; + uint8_t asic_count[AVA9_DEFAULT_MODULARS]; + + uint32_t freq_mode[AVA9_DEFAULT_MODULARS]; + int led_indicator[AVA9_DEFAULT_MODULARS]; + int fan_pct[AVA9_DEFAULT_MODULARS]; + int fan_cpm[AVA9_DEFAULT_MODULARS]; + + int temp[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT][AVA9_DEFAULT_ASIC_MAX]; + int temp_mm[AVA9_DEFAULT_MODULARS]; + + uint32_t core_volt[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT][AVA9_DEFAULT_ASIC_MAX]; + + uint8_t cutoff[AVA9_DEFAULT_MODULARS]; + int temp_target[AVA9_DEFAULT_MODULARS]; + int temp_last_max[AVA9_DEFAULT_MODULARS]; + int temp_overheat[AVA9_DEFAULT_MODULARS]; + time_t last_temp_time[AVA9_DEFAULT_MODULARS]; + + int set_voltage_level[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT]; + uint32_t set_frequency[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT][AVA9_DEFAULT_PLL_CNT]; + uint32_t get_frequency[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT][AVA9_DEFAULT_PLL_CNT]; + + uint16_t get_vin[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT]; + uint32_t get_voltage[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT]; + uint32_t get_pll[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT][AVA9_DEFAULT_PLL_CNT]; + + uint32_t get_asic[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT][AVA9_DEFAULT_ASIC_MAX][2 + AVA9_DEFAULT_PLL_CNT]; + + int8_t factory_info[AVA9_DEFAULT_FACTORY_INFO_CNT]; + int8_t overclocking_info[AVA9_DEFAULT_OVERCLOCKING_CNT]; + + uint64_t local_works[AVA9_DEFAULT_MODULARS]; + uint64_t local_works_i[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT]; + uint64_t hw_works[AVA9_DEFAULT_MODULARS]; + uint64_t hw_works_i[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT]; + uint64_t chip_matching_work[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT][AVA9_DEFAULT_ASIC_MAX]; + + uint32_t error_code[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT + 1]; + uint32_t error_crc[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_MINER_CNT]; + uint8_t error_polling_cnt[AVA9_DEFAULT_MODULARS]; + + uint8_t power_good[AVA9_DEFAULT_MODULARS]; + char pmu_version[AVA9_DEFAULT_MODULARS][AVA9_DEFAULT_PMU_CNT][5]; + uint64_t diff1[AVA9_DEFAULT_MODULARS]; + + uint16_t vin_adc_ratio[AVA9_DEFAULT_MODULARS]; + uint16_t vout_adc_ratio[AVA9_DEFAULT_MODULARS]; + + bool conn_overloaded; +}; + +struct avalon9_iic_info { + uint8_t iic_op; + union { + uint32_t aucParam[2]; + uint8_t slave_addr; + } iic_param; +}; + +struct avalon9_dev_description { + uint8_t dev_id_str[8]; + int mod_type; + uint8_t miner_count; /* it should not greater than AVA9_DEFAULT_MINER_CNT */ + uint8_t asic_count; /* asic count each miner, it should not great than AVA9_DEFAULT_ASIC_MAX */ + uint16_t vin_adc_ratio; + uint16_t vout_adc_ratio; + int set_voltage_level; + uint16_t set_freq[AVA9_DEFAULT_PLL_CNT]; +}; + +#define AVA9_WRITE_SIZE (sizeof(struct avalon9_pkg)) +#define AVA9_READ_SIZE AVA9_WRITE_SIZE + +#define AVA9_SEND_OK 0 +#define AVA9_SEND_ERROR -1 + +extern char *set_avalon9_fan(char *arg); +extern char *set_avalon9_freq(char *arg); +extern char *set_avalon9_voltage_level(char *arg); +extern char *set_avalon9_voltage_level_offset(char *arg); +extern int opt_avalon9_temp_target; +extern int opt_avalon9_polling_delay; +extern int opt_avalon9_aucspeed; +extern int opt_avalon9_aucxdelay; +extern int opt_avalon9_smart_speed; +extern bool opt_avalon9_iic_detect; +extern int opt_avalon9_freq_sel; +extern uint32_t opt_avalon9_th_pass; +extern uint32_t opt_avalon9_th_fail; +extern uint32_t opt_avalon9_th_init; +extern uint32_t opt_avalon9_th_ms; +extern uint32_t opt_avalon9_th_timeout; +extern uint32_t opt_avalon9_th_add; +extern uint32_t opt_avalon9_th_mssel; +extern uint32_t opt_avalon9_nonce_mask; +extern uint32_t opt_avalon9_nonce_check; +extern uint32_t opt_avalon9_mux_l2h; +extern uint32_t opt_avalon9_mux_h2l; +extern uint32_t opt_avalon9_h2ltime0_spd; +extern uint32_t opt_avalon9_roll_enable; +extern uint32_t opt_avalon9_spdlow; +extern uint32_t opt_avalon9_spdhigh; +extern uint32_t opt_avalon9_lv2_th_ms; +extern uint32_t opt_avalon9_lv3_th_ms; +extern uint32_t opt_avalon9_lv4_th_ms; +extern uint32_t opt_avalon9_lv5_th_ms; +extern uint32_t opt_avalon9_lv6_th_ms; +extern uint32_t opt_avalon9_lv7_th_ms; +extern uint32_t opt_avalon9_lv2_th_add; +extern uint32_t opt_avalon9_lv3_th_add; +extern uint32_t opt_avalon9_lv4_th_add; +extern uint32_t opt_avalon9_lv5_th_add; +extern uint32_t opt_avalon9_lv6_th_add; +extern uint32_t opt_avalon9_lv7_th_add; +#endif /* USE_AVALON9 */ +#endif /* _AVALON9_H_ */ diff --git a/miner.h b/miner.h index daad9dd14b..7f7b39c98e 100644 --- a/miner.h +++ b/miner.h @@ -243,6 +243,7 @@ static inline int fsync (int fd) DRIVER_ADD_COMMAND(avalon4) \ DRIVER_ADD_COMMAND(avalon7) \ DRIVER_ADD_COMMAND(avalon8) \ + DRIVER_ADD_COMMAND(avalon9) \ DRIVER_ADD_COMMAND(avalonm) \ DRIVER_ADD_COMMAND(bflsc) \ DRIVER_ADD_COMMAND(bitfury) \ @@ -1130,7 +1131,7 @@ extern pthread_cond_t restart_cond; extern void clear_stratum_shares(struct pool *pool); extern void clear_pool_work(struct pool *pool); extern void set_target(unsigned char *dest_target, double diff); -#if defined (USE_AVALON2) || defined (USE_AVALON4) || defined (USE_AVALON7) || defined (USE_AVALON8) || defined (USE_AVALON_MINER) || defined (USE_HASHRATIO) +#if defined (USE_AVALON2) || defined (USE_AVALON4) || defined (USE_AVALON7) || defined (USE_AVALON8) || defined (USE_AVALON9) || defined (USE_AVALON_MINER) || defined (USE_HASHRATIO) bool submit_nonce2_nonce(struct thr_info *thr, struct pool *pool, struct pool *real_pool, uint32_t nonce2, uint32_t nonce, uint32_t ntime); uint32_t gen_merkle_root(struct pool *pool, uint64_t nonce2); diff --git a/usbutils.c b/usbutils.c index 44f38d0237..7959595f85 100644 --- a/usbutils.c +++ b/usbutils.c @@ -72,6 +72,7 @@ static cgtimer_t usb11_cgt; #define AVALON4_TIMEOUT_MS 999 #define AVALON7_TIMEOUT_MS 999 #define AVALON8_TIMEOUT_MS 999 +#define AVALON9_TIMEOUT_MS 999 #define AVALONM_TIMEOUT_MS 999 #define KLONDIKE_TIMEOUT_MS 999 #define COINTERRA_TIMEOUT_MS 999 @@ -91,6 +92,7 @@ static cgtimer_t usb11_cgt; #define AVALON4_TIMEOUT_MS 200 #define AVALON7_TIMEOUT_MS 200 #define AVALON8_TIMEOUT_MS 200 +#define AVALON9_TIMEOUT_MS 200 #define AVALONM_TIMEOUT_MS 300 #define KLONDIKE_TIMEOUT_MS 200 #define COINTERRA_TIMEOUT_MS 200 @@ -313,6 +315,16 @@ static struct usb_intinfo ava8_ints[] = { USB_EPS(1, ava8_epinfos) }; #endif +#ifdef USE_AVALON9 +static struct usb_epinfo ava9_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 } +}; + +static struct usb_intinfo ava9_ints[] = { + USB_EPS(1, ava9_epinfos) +}; +#endif #ifdef USE_AVALON_MINER static struct usb_epinfo avam_epinfos[] = { { LIBUSB_TRANSFER_TYPE_INTERRUPT, 40, EPI(1), 0, 0 }, @@ -733,6 +745,20 @@ static struct usb_find_devices find_dev[] = { .latency = LATENCY_UNUSED, INTINFO(ava8_ints) }, #endif +#ifdef USE_AVALON9 + { + .drv = DRIVER_avalon9, + .name = "AV9", + .ident = IDENT_AV9, + .idVendor = 0x29f1, + .idProduct = 0x33f2, + .iManufacturer = "CANAAN", + .iProduct = "USB2IIC Converter", + .config = 1, + .timeout = AVALON9_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(ava9_ints) }, +#endif #ifdef USE_AVALON_MINER { .drv = DRIVER_avalonm, @@ -3840,6 +3866,7 @@ void usb_cleanup(void) case DRIVER_avalon4: case DRIVER_avalon7: case DRIVER_avalon8: + case DRIVER_avalon9: case DRIVER_avalonm: case DRIVER_klondike: case DRIVER_hashfast: diff --git a/usbutils.h b/usbutils.h index 41282b5538..36a182d67a 100644 --- a/usbutils.h +++ b/usbutils.h @@ -150,6 +150,7 @@ enum sub_ident { IDENT_AV4, IDENT_AV7, IDENT_AV8, + IDENT_AV9, IDENT_AVM, IDENT_BAJ, IDENT_BAL, @@ -392,6 +393,8 @@ struct cg_usb_info { USB_ADD_COMMAND(C_AVA7_WRITE, "Ava7Write") \ USB_ADD_COMMAND(C_AVA8_READ, "Ava8Read") \ USB_ADD_COMMAND(C_AVA8_WRITE, "Ava8Write") \ + USB_ADD_COMMAND(C_AVA9_READ, "Ava9Read") \ + USB_ADD_COMMAND(C_AVA9_WRITE, "Ava9Write") \ USB_ADD_COMMAND(C_AVAM_READ, "AvamRead") \ USB_ADD_COMMAND(C_AVAM_WRITE, "AvamWrite") \ USB_ADD_COMMAND(C_BET_WRITE, "BlockErupterWrite") \